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