Mon, 21 Jan 2013 11:16:28 -0800
Merge
jjg@1230 | 1 | /* |
jjg@1230 | 2 | * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. |
jjg@1230 | 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
jjg@1230 | 4 | * |
jjg@1230 | 5 | * This code is free software; you can redistribute it and/or modify it |
jjg@1230 | 6 | * under the terms of the GNU General Public License version 2 only, as |
jjg@1230 | 7 | * published by the Free Software Foundation. |
jjg@1230 | 8 | * |
jjg@1230 | 9 | * This code is distributed in the hope that it will be useful, but WITHOUT |
jjg@1230 | 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
jjg@1230 | 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
jjg@1230 | 12 | * version 2 for more details (a copy is included in the LICENSE file that |
jjg@1230 | 13 | * accompanied this code). |
jjg@1230 | 14 | * |
jjg@1230 | 15 | * You should have received a copy of the GNU General Public License version |
jjg@1230 | 16 | * 2 along with this work; if not, write to the Free Software Foundation, |
jjg@1230 | 17 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
jjg@1230 | 18 | * |
jjg@1230 | 19 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
jjg@1230 | 20 | * or visit www.oracle.com if you need additional information or have any |
jjg@1230 | 21 | * questions. |
jjg@1230 | 22 | */ |
jjg@1230 | 23 | |
jjg@1230 | 24 | /* |
jjg@1230 | 25 | * @test |
jjg@1407 | 26 | * @bug 7150368 8003412 |
jjg@1230 | 27 | * @summary javac should include basic ability to generate native headers |
jjg@1230 | 28 | */ |
jjg@1230 | 29 | |
jjg@1230 | 30 | import java.io.File; |
jjg@1230 | 31 | import java.io.FileWriter; |
jjg@1230 | 32 | import java.io.IOException; |
jjg@1230 | 33 | import java.lang.annotation.Annotation; |
jjg@1230 | 34 | import java.lang.annotation.Retention; |
jjg@1230 | 35 | import java.lang.annotation.RetentionPolicy; |
jjg@1230 | 36 | import java.lang.reflect.InvocationTargetException; |
jjg@1230 | 37 | import java.lang.reflect.Method; |
jjg@1230 | 38 | import java.util.ArrayList; |
jjg@1230 | 39 | import java.util.Arrays; |
jjg@1230 | 40 | import java.util.HashSet; |
jjg@1230 | 41 | import java.util.List; |
jjg@1230 | 42 | import java.util.Set; |
jjg@1230 | 43 | |
jjg@1230 | 44 | import javax.tools.StandardJavaFileManager; |
jjg@1230 | 45 | import javax.tools.StandardLocation; |
jjg@1230 | 46 | |
jjg@1230 | 47 | import com.sun.source.util.JavacTask; |
jjg@1230 | 48 | import com.sun.tools.javac.api.JavacTool; |
jjg@1230 | 49 | |
jjg@1230 | 50 | public class NativeHeaderTest { |
jjg@1230 | 51 | public static void main(String... args) throws Exception { |
jjg@1230 | 52 | new NativeHeaderTest().run(); |
jjg@1230 | 53 | } |
jjg@1230 | 54 | |
jjg@1230 | 55 | /** How to invoke javac. */ |
jjg@1230 | 56 | enum RunKind { |
jjg@1230 | 57 | /** Use the command line entry point. */ |
jjg@1230 | 58 | CMD, |
jjg@1230 | 59 | /** Use the JavaCompiler API. */ |
jjg@1230 | 60 | API |
jjg@1230 | 61 | }; |
jjg@1230 | 62 | |
jjg@1230 | 63 | /** Which classes for which to generate headers. */ |
jjg@1230 | 64 | enum GenKind { |
jjg@1230 | 65 | /** Just classes with native methods or the marker annotation. */ |
jjg@1230 | 66 | SIMPLE, |
jjg@1230 | 67 | /** All appropriate classes within the top level class. */ |
jjg@1230 | 68 | FULL |
jjg@1230 | 69 | }; |
jjg@1230 | 70 | |
jjg@1230 | 71 | // ---------- Test cases, invoked reflectively via run. ---------- |
jjg@1230 | 72 | |
jjg@1230 | 73 | @Test |
jjg@1230 | 74 | void simpleTest(RunKind rk, GenKind gk) throws Exception { |
jjg@1230 | 75 | List<File> files = new ArrayList<File>(); |
jjg@1230 | 76 | files.add(createFile("p/C.java", |
jjg@1230 | 77 | "class C { native void m(); }")); |
jjg@1230 | 78 | |
jjg@1230 | 79 | Set<String> expect = createSet("C.h"); |
jjg@1230 | 80 | |
jjg@1230 | 81 | test(rk, gk, files, expect); |
jjg@1230 | 82 | } |
jjg@1230 | 83 | |
jjg@1230 | 84 | @Test |
jjg@1230 | 85 | void nestedClassTest(RunKind rk, GenKind gk) throws Exception { |
jjg@1230 | 86 | List<File> files = new ArrayList<File>(); |
jjg@1230 | 87 | files.add(createFile("p/C.java", |
jjg@1230 | 88 | "class C { static class Inner { native void m(); } }")); |
jjg@1230 | 89 | |
jjg@1230 | 90 | Set<String> expect = createSet("C_Inner.h"); |
jjg@1230 | 91 | if (gk == GenKind.FULL) expect.add("C.h"); |
jjg@1230 | 92 | |
jjg@1230 | 93 | test(rk, gk, files, expect); |
jjg@1230 | 94 | } |
jjg@1230 | 95 | |
jjg@1230 | 96 | @Test |
jjg@1230 | 97 | void localClassTest(RunKind rk, GenKind gk) throws Exception { |
jjg@1230 | 98 | List<File> files = new ArrayList<File>(); |
jjg@1230 | 99 | files.add(createFile("p/C.java", |
jjg@1230 | 100 | "class C { native void m(); void m2() { class Local { } } }")); |
jjg@1230 | 101 | |
jjg@1230 | 102 | Set<String> expect = createSet("C.h"); |
jjg@1230 | 103 | |
jjg@1230 | 104 | test(rk, gk, files, expect); |
jjg@1230 | 105 | } |
jjg@1230 | 106 | |
jjg@1230 | 107 | @Test |
jjg@1230 | 108 | void syntheticClassTest(RunKind rk, GenKind gk) throws Exception { |
jjg@1230 | 109 | List<File> files = new ArrayList<File>(); |
jjg@1230 | 110 | files.add(createFile("p/C.java", |
jjg@1230 | 111 | "class C {\n" |
jjg@1230 | 112 | + " private C() { }\n" |
jjg@1230 | 113 | + " class Inner extends C { native void m(); }\n" |
jjg@1230 | 114 | + "}")); |
jjg@1230 | 115 | |
jjg@1230 | 116 | Set<String> expect = createSet("C_Inner.h"); |
jjg@1230 | 117 | if (gk == GenKind.FULL) expect.add("C.h"); |
jjg@1230 | 118 | |
jjg@1230 | 119 | test(rk, gk, files, expect); |
jjg@1230 | 120 | |
jjg@1230 | 121 | // double check the synthetic class was generated |
jjg@1230 | 122 | checkEqual("generatedClasses", |
jjg@1230 | 123 | createSet("C.class", "C$1.class", "C$Inner.class"), |
jjg@1230 | 124 | createSet(classesDir.list())); |
jjg@1230 | 125 | } |
jjg@1230 | 126 | |
jjg@1230 | 127 | @Test |
jjg@1407 | 128 | void oldAnnoTest(RunKind rk, GenKind gk) throws Exception { |
jjg@1230 | 129 | List<File> files = new ArrayList<File>(); |
jjg@1230 | 130 | files.add(createFile("p/C.java", |
jjg@1230 | 131 | "@javax.tools.annotation.GenerateNativeHeader class C { }")); |
jjg@1230 | 132 | |
jjg@1230 | 133 | Set<String> expect = createSet("C.h"); |
jjg@1230 | 134 | |
jjg@1230 | 135 | test(rk, gk, files, expect); |
jjg@1230 | 136 | } |
jjg@1230 | 137 | |
jjg@1230 | 138 | @Test |
jjg@1407 | 139 | void annoTest(RunKind rk, GenKind gk) throws Exception { |
jjg@1407 | 140 | List<File> files = new ArrayList<File>(); |
jjg@1407 | 141 | files.add(createFile("p/C.java", |
jjg@1407 | 142 | "class C { @java.lang.annotation.Native public static final int i = 1907; }")); |
jjg@1407 | 143 | |
jjg@1407 | 144 | Set<String> expect = createSet("C.h"); |
jjg@1407 | 145 | |
jjg@1407 | 146 | test(rk, gk, files, expect); |
jjg@1407 | 147 | } |
jjg@1407 | 148 | |
jjg@1407 | 149 | @Test |
jjg@1407 | 150 | void oldAnnoNestedClassTest(RunKind rk, GenKind gk) throws Exception { |
jjg@1407 | 151 | List<File> files = new ArrayList<File>(); |
jjg@1407 | 152 | files.add(createFile("p/C.java", |
jjg@1407 | 153 | "class C { @javax.tools.annotation.GenerateNativeHeader class Inner { } }")); |
jjg@1407 | 154 | |
jjg@1407 | 155 | Set<String> expect = createSet("C_Inner.h"); |
jjg@1407 | 156 | if (gk == GenKind.FULL) expect.add("C.h"); |
jjg@1407 | 157 | |
jjg@1407 | 158 | test(rk, gk, files, expect); |
jjg@1407 | 159 | } |
jjg@1407 | 160 | |
jjg@1407 | 161 | @Test |
jjg@1230 | 162 | void annoNestedClassTest(RunKind rk, GenKind gk) throws Exception { |
jjg@1230 | 163 | List<File> files = new ArrayList<File>(); |
jjg@1230 | 164 | files.add(createFile("p/C.java", |
jjg@1407 | 165 | "class C { class Inner { @java.lang.annotation.Native public static final int i = 1907; } }")); |
jjg@1230 | 166 | |
jjg@1230 | 167 | Set<String> expect = createSet("C_Inner.h"); |
jjg@1230 | 168 | if (gk == GenKind.FULL) expect.add("C.h"); |
jjg@1230 | 169 | |
jjg@1230 | 170 | test(rk, gk, files, expect); |
jjg@1230 | 171 | } |
jjg@1230 | 172 | |
jjg@1230 | 173 | /** |
jjg@1230 | 174 | * The worker method for each test case. |
jjg@1230 | 175 | * Compile the files and verify that exactly the expected set of header files |
jjg@1230 | 176 | * is generated. |
jjg@1230 | 177 | */ |
jjg@1230 | 178 | void test(RunKind rk, GenKind gk, List<File> files, Set<String> expect) throws Exception { |
jjg@1230 | 179 | List<String> args = new ArrayList<String>(); |
jjg@1230 | 180 | if (gk == GenKind.FULL) |
jjg@1230 | 181 | args.add("-XDjavah:full"); |
jjg@1230 | 182 | |
jjg@1230 | 183 | switch (rk) { |
jjg@1230 | 184 | case CMD: |
jjg@1230 | 185 | args.add("-d"); |
jjg@1230 | 186 | args.add(classesDir.getPath()); |
jjg@1230 | 187 | args.add("-h"); |
jjg@1230 | 188 | args.add(headersDir.getPath()); |
jjg@1230 | 189 | for (File f: files) |
jjg@1230 | 190 | args.add(f.getPath()); |
jjg@1230 | 191 | int rc = com.sun.tools.javac.Main.compile(args.toArray(new String[args.size()])); |
jjg@1230 | 192 | if (rc != 0) |
jjg@1230 | 193 | throw new Exception("compilation failed, rc=" + rc); |
jjg@1230 | 194 | break; |
jjg@1230 | 195 | |
jjg@1230 | 196 | case API: |
jjg@1230 | 197 | fm.setLocation(StandardLocation.SOURCE_PATH, Arrays.asList(srcDir)); |
jjg@1230 | 198 | fm.setLocation(StandardLocation.CLASS_OUTPUT, Arrays.asList(classesDir)); |
jjg@1230 | 199 | fm.setLocation(StandardLocation.NATIVE_HEADER_OUTPUT, Arrays.asList(headersDir)); |
jjg@1230 | 200 | JavacTask task = javac.getTask(null, fm, null, args, null, |
jjg@1230 | 201 | fm.getJavaFileObjectsFromFiles(files)); |
jjg@1230 | 202 | if (!task.call()) |
jjg@1230 | 203 | throw new Exception("compilation failed"); |
jjg@1230 | 204 | break; |
jjg@1230 | 205 | } |
jjg@1230 | 206 | |
jjg@1230 | 207 | Set<String> found = createSet(headersDir.list()); |
jjg@1230 | 208 | checkEqual("header files", expect, found); |
jjg@1230 | 209 | } |
jjg@1230 | 210 | |
jjg@1230 | 211 | /** Marker annotation for test cases. */ |
jjg@1230 | 212 | @Retention(RetentionPolicy.RUNTIME) |
jjg@1230 | 213 | @interface Test { } |
jjg@1230 | 214 | |
jjg@1230 | 215 | /** Combo test to run all test cases in all modes. */ |
jjg@1230 | 216 | void run() throws Exception { |
jjg@1230 | 217 | javac = JavacTool.create(); |
jjg@1230 | 218 | fm = javac.getStandardFileManager(null, null, null); |
jjg@1230 | 219 | |
jjg@1230 | 220 | for (RunKind rk: RunKind.values()) { |
jjg@1230 | 221 | for (GenKind gk: GenKind.values()) { |
jjg@1230 | 222 | for (Method m: getClass().getDeclaredMethods()) { |
jjg@1230 | 223 | Annotation a = m.getAnnotation(Test.class); |
jjg@1230 | 224 | if (a != null) { |
jjg@1230 | 225 | init(rk, gk, m.getName()); |
jjg@1230 | 226 | try { |
jjg@1230 | 227 | m.invoke(this, new Object[] { rk, gk }); |
jjg@1230 | 228 | } catch (InvocationTargetException e) { |
jjg@1230 | 229 | Throwable cause = e.getCause(); |
jjg@1230 | 230 | throw (cause instanceof Exception) ? ((Exception) cause) : e; |
jjg@1230 | 231 | } |
jjg@1230 | 232 | System.err.println(); |
jjg@1230 | 233 | } |
jjg@1230 | 234 | } |
jjg@1230 | 235 | } |
jjg@1230 | 236 | } |
jjg@1230 | 237 | System.err.println(testCount + " tests" + ((errorCount == 0) ? "" : ", " + errorCount + " errors")); |
jjg@1230 | 238 | if (errorCount > 0) |
jjg@1230 | 239 | throw new Exception(errorCount + " errors found"); |
jjg@1230 | 240 | } |
jjg@1230 | 241 | |
jjg@1230 | 242 | /** |
jjg@1230 | 243 | * Init directories for a test case. |
jjg@1230 | 244 | */ |
jjg@1230 | 245 | void init(RunKind rk, GenKind gk, String name) throws IOException { |
jjg@1230 | 246 | System.err.println("Test " + rk + " " + gk + " " + name); |
jjg@1230 | 247 | testCount++; |
jjg@1230 | 248 | |
jjg@1230 | 249 | testDir = new File(rk.toString().toLowerCase() + "_" + gk.toString().toLowerCase() + "-" + name); |
jjg@1230 | 250 | srcDir = new File(testDir, "src"); |
jjg@1230 | 251 | srcDir.mkdirs(); |
jjg@1230 | 252 | classesDir = new File(testDir, "classes"); |
jjg@1230 | 253 | classesDir.mkdirs(); |
jjg@1230 | 254 | headersDir = new File(testDir, "headers"); |
jjg@1230 | 255 | headersDir.mkdirs(); |
jjg@1230 | 256 | } |
jjg@1230 | 257 | |
jjg@1230 | 258 | /** Create a source file with given body text. */ |
jjg@1230 | 259 | File createFile(String path, final String body) throws IOException { |
jjg@1230 | 260 | File f = new File(srcDir, path); |
jjg@1230 | 261 | f.getParentFile().mkdirs(); |
jjg@1230 | 262 | try (FileWriter out = new FileWriter(f)) { |
jjg@1230 | 263 | out.write(body); |
jjg@1230 | 264 | } |
jjg@1230 | 265 | return f; |
jjg@1230 | 266 | } |
jjg@1230 | 267 | |
jjg@1230 | 268 | /** Convenience method to create a set of items. */ |
jjg@1230 | 269 | <T> Set<T> createSet(T... items) { |
jjg@1230 | 270 | return new HashSet<T>(Arrays.asList(items)); |
jjg@1230 | 271 | } |
jjg@1230 | 272 | |
jjg@1230 | 273 | /** Convenience method to check two values are equal, and report an error if not. */ |
jjg@1230 | 274 | <T> void checkEqual(String label, T expect, T found) { |
jjg@1230 | 275 | if ((found == null) ? (expect == null) : found.equals(expect)) |
jjg@1230 | 276 | return; |
jjg@1230 | 277 | System.err.println("Error: mismatch"); |
jjg@1230 | 278 | System.err.println(" expected: " + expect); |
jjg@1230 | 279 | System.err.println(" found: " + found); |
jjg@1230 | 280 | errorCount++; |
jjg@1230 | 281 | } |
jjg@1230 | 282 | |
jjg@1230 | 283 | // Shared across API test cases |
jjg@1230 | 284 | JavacTool javac; |
jjg@1230 | 285 | StandardJavaFileManager fm; |
jjg@1230 | 286 | |
jjg@1230 | 287 | // Directories set up by init |
jjg@1230 | 288 | File testDir; |
jjg@1230 | 289 | File srcDir; |
jjg@1230 | 290 | File classesDir; |
jjg@1230 | 291 | File headersDir; |
jjg@1230 | 292 | |
jjg@1230 | 293 | // Statistics |
jjg@1230 | 294 | int testCount; |
jjg@1230 | 295 | int errorCount; |
jjg@1230 | 296 | } |
jjg@1230 | 297 |