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