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.io.IOException;
  25 import java.io.PrintWriter;
  26 import java.io.StringWriter;
  27 import java.lang.reflect.Field;
  28 import java.lang.reflect.Method;
  29 import java.net.URL;
  30 import java.net.URLClassLoader;
  31 import java.nio.file.FileVisitResult;
  32 import java.nio.file.Files;
  33 import java.nio.file.Path;
  34 import java.nio.file.Paths;
  35 import java.nio.file.SimpleFileVisitor;
  36 import java.nio.file.attribute.BasicFileAttributes;
  37 import java.util.Objects;
  38 import java.util.spi.ToolProvider;
  39 
  40 import static org.testng.Assert.assertEquals;
  41 import static org.testng.Assert.assertNotEquals;
  42 import static org.testng.Assert.assertNotNull;
  43 import static org.testng.Assert.assertTrue;
  44 import static org.testng.Assert.fail;
  45 
  46 public class JextractToolRunner {
  47     // utilities to avoid hard-coding generated class, interface names everywhere
  48     public static String headerInterfaceName(String filename) {
  49         return staticForwarderName(filename) + "_";
  50     }
  51 
  52     public static String staticForwarderName(String filename) {
  53         int ext = filename.lastIndexOf('.');
  54         String name = ext != -1 ? filename.substring(0, ext) : filename;
  55         return name + "_h";
  56     }
  57 
  58     // struct, enum and callback interfaces are nested types of header interface
  59     public static String structInterfaceName(String headerFileName, String structName) {
  60         return headerInterfaceName(headerFileName) + "$" + structName;
  61     }
  62 
  63     public static String enumInterfaceName(String headerFileName, String enumName) {
  64         return headerInterfaceName(headerFileName) + "$" + enumName;
  65     }
  66 
  67     public static String callbackInterfaceName(String headerFileName, String fiName) {
  68         return headerInterfaceName(headerFileName) + "$" + fiName;
  69     }
  70 
  71     public static String enumForwarderInterfaceName(String headerFileName, String enumName) {
  72         return staticForwarderName(headerFileName) + "$" + enumName;
  73     }
  74 
  75     private static final ToolProvider JEXTRACT_TOOL = ToolProvider.findFirst("jextract")
  76             .orElseThrow(() ->
  77                     new RuntimeException("jextract tool not found")
  78             );
  79 
  80     private final Path inputDir;
  81     private final Path outputDir;
  82 
  83     protected JextractToolRunner() {
  84         this(null, null);
  85     }
  86 
  87     protected JextractToolRunner(Path input, Path output) {
  88         inputDir = (input != null) ? input :
  89                 Paths.get(System.getProperty("test.src", "."));
  90         outputDir = (output != null) ? output :
  91                 Paths.get(System.getProperty("test.classes", "."));
  92     }
  93 
  94     protected Path getInputFilePath(String fileName) {
  95         return inputDir.resolve(fileName).toAbsolutePath();
  96     }
  97 
  98     protected Path getOutputFilePath(String fileName) {
  99         return outputDir.resolve(fileName).toAbsolutePath();
 100     }
 101 
 102     protected static class JextractResult {
 103         private int exitCode;
 104         private String output;
 105 
 106         JextractResult(int exitCode, String output) {
 107             this.exitCode = exitCode;
 108             this.output = output;
 109         }
 110 
 111         protected JextractResult checkSuccess() {
 112             assertEquals(exitCode, 0, "Sucess excepted, failed: " + exitCode);
 113             return this;
 114         }
 115 
 116         protected JextractResult checkFailure() {
 117             assertNotEquals(exitCode, 0, "Failure excepted, succeeded!");
 118             return this;
 119         }
 120 
 121         protected JextractResult checkContainsOutput(String expected) {
 122             Objects.requireNonNull(expected);
 123             assertTrue(output.contains(expected), "Output does not contain string: " + expected);
 124             return this;
 125         }
 126 
 127         protected JextractResult checkMatchesOutput(String regex) {
 128             Objects.requireNonNull(regex);
 129             assertTrue(output.trim().matches(regex), "Output does not match regex: " + regex);
 130             return this;
 131         }
 132     }
 133 
 134     protected static JextractResult run(String... options) {
 135         StringWriter writer = new StringWriter();
 136         PrintWriter pw = new PrintWriter(writer);
 137         String[] args = new String[options.length + 1];
 138         System.arraycopy(options, 0, args, 1, options.length);
 139         args[0] = "-C-nostdinc";
 140         int result = JEXTRACT_TOOL.run(pw, pw, options);
 141         String output = writer.toString();
 142         System.err.println(output);
 143         return new JextractResult(result, output);
 144     }
 145 
 146     protected static void deleteFile(Path path) {
 147         try {
 148             Files.deleteIfExists(path);
 149         } catch (IOException ioExp) {
 150             throw new RuntimeException(ioExp);
 151         }
 152     }
 153 
 154     protected static void deleteDir(Path path) {
 155         try {
 156             Files.walkFileTree(path, new SimpleFileVisitor<>() {
 157                 @Override
 158                 public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
 159                     deleteFile(file);
 160                     return FileVisitResult.CONTINUE;
 161                 }
 162 
 163                 @Override
 164                 public FileVisitResult postVisitDirectory(Path dir, IOException exc) {
 165                     deleteFile(dir);
 166                     return FileVisitResult.CONTINUE;
 167                 }
 168             });
 169         } catch (IOException ioExp) {
 170             throw new RuntimeException(ioExp);
 171         }
 172     }
 173 
 174     protected static Loader classLoader(Path... paths) {
 175         try {
 176             URL[] urls = new URL[paths.length];
 177             for (int i = 0; i < paths.length; i++) {
 178                 urls[i] = paths[i].toUri().toURL();
 179             }
 180             URLClassLoader ucl = new URLClassLoader(urls, null);
 181             return new Loader(ucl);
 182         } catch (RuntimeException re) {
 183             throw re;
 184         } catch (Exception e) {
 185             throw new RuntimeException(e);
 186         }
 187     }
 188 
 189     protected static Field findField(Class<?> cls, String name) {
 190         try {
 191             return cls.getField(name);
 192         } catch (Exception e) {
 193             System.err.println(e);
 194             return null;
 195         }
 196     }
 197 
 198     protected static Method findMethod(Class<?> cls, String name, Class<?>... argTypes) {
 199         try {
 200             return cls.getMethod(name, argTypes);
 201         } catch (Exception e) {
 202             System.err.println(e);
 203             return null;
 204         }
 205     }
 206 
 207     protected static Method findStructFieldGet(Class<?> cls, String name) {
 208         return findMethod(cls, name + "$get");
 209     }
 210 
 211     protected static Method findStructFieldSet(Class<?> cls, String name, Class<?> type) {
 212         return findMethod(cls, name + "$set", type);
 213     }
 214 
 215     protected static Method findStructFieldPointerGet(Class<?> cls, String name) {
 216         return findMethod(cls, name + "$ptr");
 217     }
 218 
 219     protected static Method findGlobalVariableGet(Class<?> cls, String name) {
 220         return findMethod(cls, name + "$get");
 221     }
 222 
 223     protected static Method findGlobalVariableSet(Class<?> cls, String name, Class<?> type) {
 224         return findMethod(cls, name + "$set", type);
 225     }
 226 
 227     protected static Method findGlobalVariablePointerGet(Class<?> cls, String name) {
 228         return findMethod(cls, name + "$ptr");
 229     }
 230 
 231     protected static Method findEnumConstGet(Class<?> cls, String name) {
 232         return findMethod(cls, name);
 233     }
 234 
 235     protected static Method findFirstMethod(Class<?> cls, String name) {
 236         try {
 237             for (Method m : cls.getMethods()) {
 238                 if (name.equals(m.getName())) {
 239                     return m;
 240                 }
 241             }
 242             return null;
 243         } catch (Exception e) {
 244             System.err.println(e);
 245             return null;
 246         }
 247     }
 248 
 249     protected Field checkIntField(Class<?> cls, String name, int value) {
 250         Field field = findField(cls, name);
 251         assertNotNull(field);
 252         assertEquals(field.getType(), int.class);
 253         try {
 254             assertEquals((int)field.get(null), value);
 255         } catch (Exception exp) {
 256             System.err.println(exp);
 257             assertTrue(false, "should not reach here");
 258         }
 259         return field;
 260     }
 261 
 262     protected Class<?> findClass(Class<?>[] clz, String name) {
 263         for (Class<?> cls: clz) {
 264             if (cls.getSimpleName().equals(name)) {
 265                 return cls;
 266             }
 267         }
 268         return null;
 269     }
 270 
 271     protected Method checkMethod(Class<?> cls, String name, Class<?> returnType, Class<?>... args) {
 272         try {
 273             Method m = cls.getDeclaredMethod(name, args);
 274             assertTrue(m.getReturnType() == returnType);
 275             return m;
 276         } catch (NoSuchMethodException nsme) {
 277             fail("Expect method " + name);
 278         }
 279         return null;
 280     }
 281 
 282     protected static class Loader implements AutoCloseable {
 283 
 284         private final URLClassLoader loader;
 285 
 286         public Loader(URLClassLoader loader) {
 287             this.loader = loader;
 288         }
 289 
 290         public Class<?> loadClass(String className) {
 291             try {
 292                 return Class.forName(className, false, loader);
 293             } catch (ClassNotFoundException e) {
 294                 // return null so caller can check if class loading
 295                 // was successful with assertNotNull/assertNull
 296                 return null;
 297             }
 298         }
 299 
 300         @Override
 301         public void close() {
 302             try {
 303                 loader.close();
 304             } catch (IOException e) {
 305                 throw new RuntimeException(e);
 306             }
 307         }
 308     }
 309 }