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