1 /*
2  * Copyright (c) 2013, 2015, 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 com.sun.tools.javac.api.*;
25 import com.sun.tools.javac.file.*;
26 import java.io.*;
27 import java.util.*;
28 import javax.tools.*;
29 
30 // More general parameter limit testing framework, and designed so
31 // that it could be expanded into a general limits-testing framework
32 // in the future.
33 public class NumArgsTest {
34 
35     private static final NumArgsTest.NestingDef[] NO_NESTING = {};
36 
37     // threshold is named as such because "threshold" args is expected
38     // to pass, and "threshold" + 1 args is expected to fail.
39     private final int threshold;
40     private final boolean isStaticMethod;
41     private final String result;
42     private final String testName;
43     private final String methodName;
44     private final NestingDef[] nesting;
45     private final File testdir;
46     private final JavacTool tool = JavacTool.create();
47     private final JavacFileManager fm =
48         tool.getStandardFileManager(null, null, null);
49     private int errors = 0;
50 
NumArgsTest(final int threshold, final boolean isStaticMethod, final String result, final String methodName, final String testName, final NestingDef[] nesting)51     public NumArgsTest(final int threshold,
52                        final boolean isStaticMethod,
53                        final String result,
54                        final String methodName,
55                        final String testName,
56                        final NestingDef[] nesting) {
57         this.threshold = threshold;
58         this.isStaticMethod = isStaticMethod;
59         this.result = result;
60         this.methodName = methodName;
61         this.testName = testName;
62         this.nesting = nesting;
63         testdir = new File(testName);
64         testdir.mkdir();
65     }
66 
NumArgsTest(final int threshold, final boolean isStaticMethod, final String result, final String methodName, final String testName)67     public NumArgsTest(final int threshold,
68                        final boolean isStaticMethod,
69                        final String result,
70                        final String methodName,
71                        final String testName) {
72         this(threshold, isStaticMethod, result, methodName,
73              testName, NO_NESTING);
74     }
75 
NumArgsTest(final int threshold, final String result, final String methodName, final String testName, final NestingDef[] nesting)76     public NumArgsTest(final int threshold,
77                        final String result,
78                        final String methodName,
79                        final String testName,
80                        final NestingDef[] nesting) {
81         this(threshold, false, result, methodName, testName, nesting);
82     }
83 
NumArgsTest(final int threshold, final String result, final String methodName, final String testName)84     public NumArgsTest(final int threshold,
85                        final String result,
86                        final String methodName,
87                        final String testName) {
88         this(threshold, false, result, methodName, testName, NO_NESTING);
89     }
90 
NumArgsTest(final int threshold, final String testName, final NestingDef[] nesting)91     public NumArgsTest(final int threshold,
92                        final String testName,
93                        final NestingDef[] nesting) {
94         this(threshold, null, null, testName, nesting);
95     }
96 
NumArgsTest(final int threshold, final String testName)97     public NumArgsTest(final int threshold,
98                        final String testName) {
99         this(threshold, null, null, testName, NO_NESTING);
100     }
101 
NumArgsTest(final int threshold, final String testName, final String constructorName, final NestingDef[] nesting)102     public NumArgsTest(final int threshold,
103                        final String testName,
104                        final String constructorName,
105                        final NestingDef[] nesting) {
106         this(threshold, null, constructorName, testName, nesting);
107     }
108 
writeArgs(final int num, final PrintWriter stream)109     protected void writeArgs(final int num, final PrintWriter stream)
110         throws IOException {
111         stream.print("int x1");
112         for(int i = 1; i < num; i++)
113             stream.print(", int x" + (i + 1));
114     }
115 
writeMethod(final int num, final String name, final PrintWriter stream)116     protected void writeMethod(final int num,
117                                final String name,
118                                final PrintWriter stream)
119         throws IOException {
120         stream.write("public ");
121         if (isStaticMethod) stream.write("static ");
122         if (result == null)
123             stream.write("");
124         else {
125             stream.write(result);
126             stream.write(" ");
127         }
128         stream.write(name);
129         stream.write("(");
130         writeArgs(num, stream);
131         stream.write(") {}\n");
132     }
133 
writeJavaFile(final int num, final boolean pass, final PrintWriter stream)134     protected void writeJavaFile(final int num,
135                                  final boolean pass,
136                                  final PrintWriter stream)
137         throws IOException {
138         final String fullName = testName + (pass ? "Pass" : "Fail");
139         stream.write("public class ");
140         stream.write(fullName);
141         stream.write(" {\n");
142         for(int i = 0; i < nesting.length; i++)
143             nesting[i].writeBefore(stream);
144         if (null == methodName)
145             writeMethod(num, fullName, stream);
146         else
147             writeMethod(num, methodName, stream);
148         for(int i = nesting.length - 1; i >= 0; i--)
149             nesting[i].writeAfter(stream);
150         stream.write("}\n");
151     }
152 
runTest()153     public void runTest() throws Exception {
154         // Run the pass test
155         final String passTestName = testName + "Pass.java";
156         final StringWriter passBody = new StringWriter();
157         final PrintWriter passStream = new PrintWriter(passBody);
158         final File passFile = new File(testdir, passTestName);
159         final FileWriter passWriter = new FileWriter(passFile);
160 
161         writeJavaFile(threshold, true, passStream);
162         passStream.close();
163         passWriter.write(passBody.toString());
164         passWriter.close();
165 
166         final StringWriter passSW = new StringWriter();
167         final String[] passArgs = { passFile.toString() };
168         final Iterable<? extends JavaFileObject> passFiles =
169             fm.getJavaFileObjectsFromFiles(Arrays.asList(passFile));
170         final JavaCompiler.CompilationTask passTask =
171             tool.getTask(passSW, fm, null, null, null, passFiles);
172 
173         if (!passTask.call()) {
174             errors++;
175             System.err.println("Compilation unexpectedly failed. Body:\n" +
176                                passBody);
177             System.err.println("Output:\n" + passSW.toString());
178         }
179 
180         // Run the fail test
181         final String failTestName = testName + "Fail.java";
182         final StringWriter failBody = new StringWriter();
183         final PrintWriter failStream = new PrintWriter(failBody);
184         final File failFile = new File(testdir, failTestName);
185         final FileWriter failWriter = new FileWriter(failFile);
186 
187         writeJavaFile(threshold + 1, false, failStream);
188         failStream.close();
189         failWriter.write(failBody.toString());
190         failWriter.close();
191 
192         final StringWriter failSW = new StringWriter();
193         final TestDiagnosticHandler failDiag =
194             new TestDiagnosticHandler("compiler.err.limit.parameters");
195         final Iterable<? extends JavaFileObject> failFiles =
196             fm.getJavaFileObjectsFromFiles(Arrays.asList(failFile));
197         final JavaCompiler.CompilationTask failTask =
198             tool.getTask(failSW,
199                          tool.getStandardFileManager(null, null, null),
200                          failDiag,
201                          null,
202                          null,
203                          failFiles);
204 
205         if (failTask.call()) {
206             errors++;
207             System.err.println("Compilation unexpectedly succeeded.");
208             System.err.println("Input:\n" + failBody);
209         }
210 
211         if (!failDiag.sawError) {
212             errors++;
213             System.err.println("Did not see expected compile error.");
214         }
215 
216         if (errors != 0)
217             throw new RuntimeException("Test failed with " +
218                                        errors + " errors");
219     }
220 
classNesting(final String name)221     public static NestingDef classNesting(final String name) {
222         return new NestedClassBuilder(name, false);
223     }
224 
classNesting(final String name, final boolean isStatic)225     public static NestingDef classNesting(final String name,
226                                           final boolean isStatic) {
227         return new NestedClassBuilder(name, isStatic);
228     }
229 
230     protected interface NestingDef {
writeBefore(final PrintWriter stream)231         public abstract void writeBefore(final PrintWriter stream);
writeAfter(final PrintWriter stream)232         public abstract void writeAfter(final PrintWriter stream);
233     }
234 
235     private static class NestedClassBuilder implements NestingDef {
236         private final String name;
237         private final boolean isStatic;
NestedClassBuilder(final String name, final boolean isStatic)238         public NestedClassBuilder(final String name, final boolean isStatic) {
239             this.name = name;
240             this.isStatic = isStatic;
241         }
writeBefore(final PrintWriter stream)242         public void writeBefore(final PrintWriter stream) {
243             stream.write("public ");
244             if (isStatic) stream.write("static");
245             stream.write(" class ");
246             stream.write(name);
247             stream.write(" {\n");
248         }
writeAfter(final PrintWriter stream)249         public void writeAfter(final PrintWriter stream) {
250             stream.write("}\n");
251         }
252     }
253 
254     public class TestDiagnosticHandler<T> implements DiagnosticListener<T> {
255         public boolean sawError;
256         public final String target;
257 
TestDiagnosticHandler(final String target)258         public TestDiagnosticHandler(final String target) {
259             this.target = target;
260         }
261 
report(final Diagnostic<? extends T> diag)262         public void report(final Diagnostic<? extends T> diag) {
263             if (diag.getCode().equals(target))
264                 sawError = true;
265         }
266     }
267 
268 }
269