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