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