jjg@1230: /* jjg@1230: * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. jjg@1230: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. jjg@1230: * jjg@1230: * This code is free software; you can redistribute it and/or modify it jjg@1230: * under the terms of the GNU General Public License version 2 only, as jjg@1230: * published by the Free Software Foundation. jjg@1230: * jjg@1230: * This code is distributed in the hope that it will be useful, but WITHOUT jjg@1230: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or jjg@1230: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License jjg@1230: * version 2 for more details (a copy is included in the LICENSE file that jjg@1230: * accompanied this code). jjg@1230: * jjg@1230: * You should have received a copy of the GNU General Public License version jjg@1230: * 2 along with this work; if not, write to the Free Software Foundation, jjg@1230: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. jjg@1230: * jjg@1230: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA jjg@1230: * or visit www.oracle.com if you need additional information or have any jjg@1230: * questions. jjg@1230: */ jjg@1230: jjg@1230: /* jjg@1230: * @test jjg@1407: * @bug 7150368 8003412 jjg@1230: * @summary javac should include basic ability to generate native headers jjg@1230: */ jjg@1230: jjg@1230: import java.io.File; jjg@1230: import java.io.FileWriter; jjg@1230: import java.io.IOException; jjg@1230: import java.lang.annotation.Annotation; jjg@1230: import java.lang.annotation.Retention; jjg@1230: import java.lang.annotation.RetentionPolicy; jjg@1230: import java.lang.reflect.InvocationTargetException; jjg@1230: import java.lang.reflect.Method; jjg@1230: import java.util.ArrayList; jjg@1230: import java.util.Arrays; jjg@1230: import java.util.HashSet; jjg@1230: import java.util.List; jjg@1230: import java.util.Set; jjg@1230: jjg@1230: import javax.tools.StandardJavaFileManager; jjg@1230: import javax.tools.StandardLocation; jjg@1230: jjg@1230: import com.sun.source.util.JavacTask; jjg@1230: import com.sun.tools.javac.api.JavacTool; jjg@1230: jjg@1230: public class NativeHeaderTest { jjg@1230: public static void main(String... args) throws Exception { jjg@1230: new NativeHeaderTest().run(); jjg@1230: } jjg@1230: jjg@1230: /** How to invoke javac. */ jjg@1230: enum RunKind { jjg@1230: /** Use the command line entry point. */ jjg@1230: CMD, jjg@1230: /** Use the JavaCompiler API. */ jjg@1230: API jjg@1230: }; jjg@1230: jjg@1230: /** Which classes for which to generate headers. */ jjg@1230: enum GenKind { jjg@1230: /** Just classes with native methods or the marker annotation. */ jjg@1230: SIMPLE, jjg@1230: /** All appropriate classes within the top level class. */ jjg@1230: FULL jjg@1230: }; jjg@1230: jjg@1230: // ---------- Test cases, invoked reflectively via run. ---------- jjg@1230: jjg@1230: @Test jjg@1230: void simpleTest(RunKind rk, GenKind gk) throws Exception { jjg@1230: List files = new ArrayList(); jjg@1230: files.add(createFile("p/C.java", jjg@1230: "class C { native void m(); }")); jjg@1230: jjg@1230: Set expect = createSet("C.h"); jjg@1230: jjg@1230: test(rk, gk, files, expect); jjg@1230: } jjg@1230: jjg@1230: @Test jjg@1230: void nestedClassTest(RunKind rk, GenKind gk) throws Exception { jjg@1230: List files = new ArrayList(); jjg@1230: files.add(createFile("p/C.java", jjg@1230: "class C { static class Inner { native void m(); } }")); jjg@1230: jjg@1230: Set expect = createSet("C_Inner.h"); jjg@1230: if (gk == GenKind.FULL) expect.add("C.h"); jjg@1230: jjg@1230: test(rk, gk, files, expect); jjg@1230: } jjg@1230: jjg@1230: @Test jjg@1230: void localClassTest(RunKind rk, GenKind gk) throws Exception { jjg@1230: List files = new ArrayList(); jjg@1230: files.add(createFile("p/C.java", jjg@1230: "class C { native void m(); void m2() { class Local { } } }")); jjg@1230: jjg@1230: Set expect = createSet("C.h"); jjg@1230: jjg@1230: test(rk, gk, files, expect); jjg@1230: } jjg@1230: jjg@1230: @Test jjg@1230: void syntheticClassTest(RunKind rk, GenKind gk) throws Exception { jjg@1230: List files = new ArrayList(); jjg@1230: files.add(createFile("p/C.java", jjg@1230: "class C {\n" jjg@1230: + " private C() { }\n" jjg@1230: + " class Inner extends C { native void m(); }\n" jjg@1230: + "}")); jjg@1230: jjg@1230: Set expect = createSet("C_Inner.h"); jjg@1230: if (gk == GenKind.FULL) expect.add("C.h"); jjg@1230: jjg@1230: test(rk, gk, files, expect); jjg@1230: jjg@1230: // double check the synthetic class was generated jjg@1230: checkEqual("generatedClasses", jjg@1230: createSet("C.class", "C$1.class", "C$Inner.class"), jjg@1230: createSet(classesDir.list())); jjg@1230: } jjg@1230: jjg@1230: @Test jjg@1407: void oldAnnoTest(RunKind rk, GenKind gk) throws Exception { jjg@1230: List files = new ArrayList(); jjg@1230: files.add(createFile("p/C.java", jjg@1230: "@javax.tools.annotation.GenerateNativeHeader class C { }")); jjg@1230: jjg@1230: Set expect = createSet("C.h"); jjg@1230: jjg@1230: test(rk, gk, files, expect); jjg@1230: } jjg@1230: jjg@1230: @Test jjg@1407: void annoTest(RunKind rk, GenKind gk) throws Exception { jjg@1407: List files = new ArrayList(); jjg@1407: files.add(createFile("p/C.java", jjg@1407: "class C { @java.lang.annotation.Native public static final int i = 1907; }")); jjg@1407: jjg@1407: Set expect = createSet("C.h"); jjg@1407: jjg@1407: test(rk, gk, files, expect); jjg@1407: } jjg@1407: jjg@1407: @Test jjg@1407: void oldAnnoNestedClassTest(RunKind rk, GenKind gk) throws Exception { jjg@1407: List files = new ArrayList(); jjg@1407: files.add(createFile("p/C.java", jjg@1407: "class C { @javax.tools.annotation.GenerateNativeHeader class Inner { } }")); jjg@1407: jjg@1407: Set expect = createSet("C_Inner.h"); jjg@1407: if (gk == GenKind.FULL) expect.add("C.h"); jjg@1407: jjg@1407: test(rk, gk, files, expect); jjg@1407: } jjg@1407: jjg@1407: @Test jjg@1230: void annoNestedClassTest(RunKind rk, GenKind gk) throws Exception { jjg@1230: List files = new ArrayList(); jjg@1230: files.add(createFile("p/C.java", jjg@1407: "class C { class Inner { @java.lang.annotation.Native public static final int i = 1907; } }")); jjg@1230: jjg@1230: Set expect = createSet("C_Inner.h"); jjg@1230: if (gk == GenKind.FULL) expect.add("C.h"); jjg@1230: jjg@1230: test(rk, gk, files, expect); jjg@1230: } jjg@1230: jjg@1230: /** jjg@1230: * The worker method for each test case. jjg@1230: * Compile the files and verify that exactly the expected set of header files jjg@1230: * is generated. jjg@1230: */ jjg@1230: void test(RunKind rk, GenKind gk, List files, Set expect) throws Exception { jjg@1230: List args = new ArrayList(); jjg@1230: if (gk == GenKind.FULL) jjg@1230: args.add("-XDjavah:full"); jjg@1230: jjg@1230: switch (rk) { jjg@1230: case CMD: jjg@1230: args.add("-d"); jjg@1230: args.add(classesDir.getPath()); jjg@1230: args.add("-h"); jjg@1230: args.add(headersDir.getPath()); jjg@1230: for (File f: files) jjg@1230: args.add(f.getPath()); jjg@1230: int rc = com.sun.tools.javac.Main.compile(args.toArray(new String[args.size()])); jjg@1230: if (rc != 0) jjg@1230: throw new Exception("compilation failed, rc=" + rc); jjg@1230: break; jjg@1230: jjg@1230: case API: jjg@1230: fm.setLocation(StandardLocation.SOURCE_PATH, Arrays.asList(srcDir)); jjg@1230: fm.setLocation(StandardLocation.CLASS_OUTPUT, Arrays.asList(classesDir)); jjg@1230: fm.setLocation(StandardLocation.NATIVE_HEADER_OUTPUT, Arrays.asList(headersDir)); jjg@1230: JavacTask task = javac.getTask(null, fm, null, args, null, jjg@1230: fm.getJavaFileObjectsFromFiles(files)); jjg@1230: if (!task.call()) jjg@1230: throw new Exception("compilation failed"); jjg@1230: break; jjg@1230: } jjg@1230: jjg@1230: Set found = createSet(headersDir.list()); jjg@1230: checkEqual("header files", expect, found); jjg@1230: } jjg@1230: jjg@1230: /** Marker annotation for test cases. */ jjg@1230: @Retention(RetentionPolicy.RUNTIME) jjg@1230: @interface Test { } jjg@1230: jjg@1230: /** Combo test to run all test cases in all modes. */ jjg@1230: void run() throws Exception { jjg@1230: javac = JavacTool.create(); jjg@1230: fm = javac.getStandardFileManager(null, null, null); jjg@1230: jjg@1230: for (RunKind rk: RunKind.values()) { jjg@1230: for (GenKind gk: GenKind.values()) { jjg@1230: for (Method m: getClass().getDeclaredMethods()) { jjg@1230: Annotation a = m.getAnnotation(Test.class); jjg@1230: if (a != null) { jjg@1230: init(rk, gk, m.getName()); jjg@1230: try { jjg@1230: m.invoke(this, new Object[] { rk, gk }); jjg@1230: } catch (InvocationTargetException e) { jjg@1230: Throwable cause = e.getCause(); jjg@1230: throw (cause instanceof Exception) ? ((Exception) cause) : e; jjg@1230: } jjg@1230: System.err.println(); jjg@1230: } jjg@1230: } jjg@1230: } jjg@1230: } jjg@1230: System.err.println(testCount + " tests" + ((errorCount == 0) ? "" : ", " + errorCount + " errors")); jjg@1230: if (errorCount > 0) jjg@1230: throw new Exception(errorCount + " errors found"); jjg@1230: } jjg@1230: jjg@1230: /** jjg@1230: * Init directories for a test case. jjg@1230: */ jjg@1230: void init(RunKind rk, GenKind gk, String name) throws IOException { jjg@1230: System.err.println("Test " + rk + " " + gk + " " + name); jjg@1230: testCount++; jjg@1230: jjg@1230: testDir = new File(rk.toString().toLowerCase() + "_" + gk.toString().toLowerCase() + "-" + name); jjg@1230: srcDir = new File(testDir, "src"); jjg@1230: srcDir.mkdirs(); jjg@1230: classesDir = new File(testDir, "classes"); jjg@1230: classesDir.mkdirs(); jjg@1230: headersDir = new File(testDir, "headers"); jjg@1230: headersDir.mkdirs(); jjg@1230: } jjg@1230: jjg@1230: /** Create a source file with given body text. */ jjg@1230: File createFile(String path, final String body) throws IOException { jjg@1230: File f = new File(srcDir, path); jjg@1230: f.getParentFile().mkdirs(); jjg@1230: try (FileWriter out = new FileWriter(f)) { jjg@1230: out.write(body); jjg@1230: } jjg@1230: return f; jjg@1230: } jjg@1230: jjg@1230: /** Convenience method to create a set of items. */ jjg@1230: Set createSet(T... items) { jjg@1230: return new HashSet(Arrays.asList(items)); jjg@1230: } jjg@1230: jjg@1230: /** Convenience method to check two values are equal, and report an error if not. */ jjg@1230: void checkEqual(String label, T expect, T found) { jjg@1230: if ((found == null) ? (expect == null) : found.equals(expect)) jjg@1230: return; jjg@1230: System.err.println("Error: mismatch"); jjg@1230: System.err.println(" expected: " + expect); jjg@1230: System.err.println(" found: " + found); jjg@1230: errorCount++; jjg@1230: } jjg@1230: jjg@1230: // Shared across API test cases jjg@1230: JavacTool javac; jjg@1230: StandardJavaFileManager fm; jjg@1230: jjg@1230: // Directories set up by init jjg@1230: File testDir; jjg@1230: File srcDir; jjg@1230: File classesDir; jjg@1230: File headersDir; jjg@1230: jjg@1230: // Statistics jjg@1230: int testCount; jjg@1230: int errorCount; jjg@1230: } jjg@1230: