| /******************************************************************************* |
| * Copyright (c) 2017, 2021 GK Software AG and others. |
| * |
| * This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * Stephan Herrmann - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.jdt.core.tests.compiler.regression; |
| |
| import java.io.BufferedReader; |
| import java.io.File; |
| import java.io.FileNotFoundException; |
| import java.io.FileOutputStream; |
| import java.io.IOException; |
| import java.io.PrintWriter; |
| import java.io.StringReader; |
| import java.util.ArrayList; |
| |
| import org.eclipse.jdt.core.JavaCore; |
| import org.eclipse.jdt.core.compiler.CompilationProgress; |
| import org.eclipse.jdt.core.compiler.batch.BatchCompiler; |
| import org.eclipse.jdt.core.tests.util.Util; |
| import org.eclipse.jdt.internal.compiler.batch.ClasspathLocation; |
| import org.eclipse.jdt.internal.compiler.batch.FileSystem; |
| import org.eclipse.jdt.internal.compiler.batch.Main; |
| |
| public abstract class AbstractBatchCompilerTest extends AbstractRegressionTest { |
| |
| protected static abstract class Matcher { |
| abstract boolean match(String effective); |
| abstract String expected(); // for use in JUnit comparison framework |
| } |
| |
| /** |
| * Used for preview features especially. |
| * @param compilerVersion - CompilerOptions.version string |
| * @return true if spec version is same as compiler version |
| */ |
| public static boolean isJREVersionEqualTo(String compilerVersion) { |
| String specVersion = System.getProperty("java.specification.version"); |
| return specVersion != null && Integer.valueOf(specVersion) == Integer.valueOf(compilerVersion); |
| } |
| /** |
| * Abstract normalizer for output comparison. This class merely embodies a |
| * chain of responsibility, plus the signature of the method of interest |
| * here, that is {@link #normalized(String) normalized}. |
| */ |
| protected static abstract class Normalizer { |
| private Normalizer nextInChain; |
| Normalizer(Normalizer nextInChain) { |
| this.nextInChain = nextInChain; |
| } |
| String normalized(String originalValue) { |
| String result; |
| if (this.nextInChain == null) |
| result = Util.convertToIndependantLineDelimiter(originalValue); |
| else |
| result = this.nextInChain.normalized(originalValue); |
| return result; |
| } |
| } |
| |
| /** |
| * This normalizer replaces occurrences of a given string with a given |
| * placeholder. |
| */ |
| protected static class StringNormalizer extends Normalizer { |
| private String match; |
| private int matchLength; |
| private String placeholder; |
| StringNormalizer(Normalizer nextInChain, String match, String placeholder) { |
| super(nextInChain); |
| this.match = match; |
| this.matchLength = match.length(); |
| this.placeholder = placeholder; |
| } |
| @Override |
| String normalized(String originalValue) { |
| String result; |
| StringBuffer normalizedValueBuffer = new StringBuffer(originalValue); |
| int nextOccurrenceIndex; |
| while ((nextOccurrenceIndex = normalizedValueBuffer.indexOf(this.match)) != -1) |
| normalizedValueBuffer.replace(nextOccurrenceIndex, |
| nextOccurrenceIndex + this.matchLength, this.placeholder); |
| result = super.normalized(normalizedValueBuffer.toString()); |
| return result; |
| } |
| } |
| |
| protected static class TestCompilationProgress extends CompilationProgress { |
| boolean isCanceled = false; |
| int workedSoFar = 0; |
| StringBuffer buffer = new StringBuffer(); |
| public void begin(int remainingWork) { |
| this.buffer.append("----------\n[worked: 0 - remaining: ").append(remainingWork).append("]\n"); |
| } |
| public void done() { |
| this.buffer.append("----------\n"); |
| } |
| public boolean isCanceled() { |
| return this.isCanceled; |
| } |
| public void setTaskName(String name) { |
| this.buffer.append(name).append('\n'); |
| } |
| public String toString() { |
| return this.buffer.toString(); |
| } |
| public void worked(int workIncrement, int remainingWork) { |
| this.workedSoFar += workIncrement; |
| this.buffer.append("[worked: ").append(this.workedSoFar).append(" - remaining: ").append(remainingWork).append("]\n"); |
| } |
| } |
| |
| public static final String OUTPUT_DIR_PLACEHOLDER = "---OUTPUT_DIR_PLACEHOLDER---"; |
| public static final String LIB_DIR_PLACEHOLDER = "---LIB_DIR_PLACEHOLDER---"; |
| |
| /** |
| * Normalizer instance that replaces occurrences of OUTPUT_DIR with |
| * OUTPUT_DIR_PLACEHOLDER and changes file separator to / if the |
| * platform file separator is different from /. |
| */ |
| protected static Normalizer outputDirNormalizer; |
| static { |
| if (File.separatorChar == '/') { |
| outputDirNormalizer = |
| new StringNormalizer( |
| new StringNormalizer( |
| null, OUTPUT_DIR, OUTPUT_DIR_PLACEHOLDER), |
| LIB_DIR, LIB_DIR_PLACEHOLDER); |
| } |
| else { |
| outputDirNormalizer = |
| new StringNormalizer( |
| new StringNormalizer( |
| new StringNormalizer( |
| null, File.separator, "/"), |
| OUTPUT_DIR, OUTPUT_DIR_PLACEHOLDER), |
| LIB_DIR, LIB_DIR_PLACEHOLDER); |
| } |
| } |
| |
| public AbstractBatchCompilerTest(String name) { |
| super(name); |
| } |
| |
| protected static final String JRE_HOME_DIR = Util.getJREDirectory(); |
| protected static final Main MAIN = new Main(null/*outWriter*/, null/*errWriter*/, false/*systemExit*/, null/*options*/, null/*progress*/); |
| |
| private static boolean CASCADED_JARS_CREATED; |
| @Override |
| protected void setUp() throws Exception { |
| super.setUp(); |
| CASCADED_JARS_CREATED = false; // initialization needed for each subclass individually |
| } |
| |
| protected void createCascadedJars() { |
| if (!CASCADED_JARS_CREATED) { |
| File libDir = new File(LIB_DIR); |
| Util.delete(libDir); // make sure we recycle the libs |
| libDir.mkdirs(); |
| try { |
| Util.createJar( |
| new String[] { |
| "p/A.java", |
| "package p;\n" + |
| "public class A {\n" + |
| "}", |
| }, |
| new String[] { |
| "META-INF/MANIFEST.MF", |
| "Manifest-Version: 1.0\n" + |
| "Created-By: Eclipse JDT Test Harness\n" + |
| "Class-Path: lib2.jar\n", |
| "p/S1.java", |
| "package p;\n" + |
| "public class S1 {\n" + |
| "}", |
| }, |
| LIB_DIR + "/lib1.jar", |
| JavaCore.VERSION_1_4); |
| Util.createJar( |
| new String[] { |
| "p/B.java", |
| "package p;\n" + |
| "public class B {\n" + |
| "}", |
| "p/R.java", |
| "package p;\n" + |
| "public class R {\n" + |
| " public static final int R2 = 2;\n" + |
| "}", |
| }, |
| new String[] { |
| "p/S2.java", |
| "package p;\n" + |
| "public class S2 {\n" + |
| "}", |
| }, |
| LIB_DIR + "/lib2.jar", |
| JavaCore.VERSION_1_4); |
| Util.createJar( |
| new String[] { |
| "p/C.java", |
| "package p;\n" + |
| "public class C {\n" + |
| "}", |
| "p/R.java", |
| "package p;\n" + |
| "public class R {\n" + |
| " public static final int R3 = 3;\n" + |
| "}", |
| }, |
| new String[] { |
| "META-INF/MANIFEST.MF", |
| "Manifest-Version: 1.0\n" + |
| "Created-By: Eclipse JDT Test Harness\n" + |
| "Class-Path: lib4.jar\n", |
| }, |
| LIB_DIR + "/lib3.jar", |
| JavaCore.VERSION_1_4); |
| Util.createJar( |
| new String[] { |
| "p/D.java", |
| "package p;\n" + |
| "public class D {\n" + |
| "}", |
| }, |
| new String[] { |
| "META-INF/MANIFEST.MF", |
| "Manifest-Version: 1.0\n" + |
| "Created-By: Eclipse JDT Test Harness\n" + |
| "Class-Path: lib1.jar lib3.jar\n", |
| }, |
| LIB_DIR + "/lib4.jar", |
| JavaCore.VERSION_1_4); |
| Util.createJar( |
| new String[] { |
| "p/C.java", |
| "package p;\n" + |
| "public class C {\n" + |
| "}", |
| "p/R.java", |
| "package p;\n" + |
| "public class R {\n" + |
| " public static final int R3 = 3;\n" + |
| "}", |
| }, |
| new String[] { |
| "META-INF/MANIFEST.MF", |
| "Manifest-Version: 1.0\n" + |
| "Created-By: Eclipse JDT Test Harness\n" + |
| "Class-Path: s/lib6.jar\n", |
| }, |
| LIB_DIR + "/lib5.jar", |
| JavaCore.VERSION_1_4); |
| new File(LIB_DIR + "/s").mkdir(); |
| Util.createJar( |
| new String[] { |
| "p/D.java", |
| "package p;\n" + |
| "public class D {\n" + |
| "}", |
| }, |
| new String[] { |
| "META-INF/MANIFEST.MF", |
| "Manifest-Version: 1.0\n" + |
| "Created-By: Eclipse JDT Test Harness\n" + |
| "Class-Path: ../lib7.jar\n", |
| }, |
| LIB_DIR + "/s/lib6.jar", |
| JavaCore.VERSION_1_4); |
| Util.createJar( |
| new String[] { |
| "p/A.java", |
| "package p;\n" + |
| "public class A {\n" + |
| "}", |
| }, |
| new String[] { |
| "META-INF/MANIFEST.MF", |
| "Manifest-Version: 1.0\n" + |
| "Created-By: Eclipse JDT Test Harness\n" + |
| "Class-Path: lib2.jar\n", |
| }, |
| LIB_DIR + "/lib7.jar", |
| JavaCore.VERSION_1_4); |
| Util.createJar( |
| new String[] { |
| "p/F.java", |
| "package p;\n" + |
| "public class F {\n" + |
| "}", |
| }, |
| new String[] { |
| "META-INF/MANIFEST.MF", |
| "Manifest-Version: 1.0\n" + |
| "Created-By: Eclipse JDT Test Harness\n" + |
| "Class-Path: " + LIB_DIR + "/lib3.jar lib1.jar\n", |
| }, |
| LIB_DIR + "/lib8.jar", |
| JavaCore.VERSION_1_4); |
| Util.createJar( |
| new String[] { |
| "p/G.java", |
| "package p;\n" + |
| "public class G {\n" + |
| "}", |
| }, |
| new String[] { |
| "META-INF/MANIFEST.MF", |
| "Manifest-Version: 1.0\n" + |
| "Created-By: Eclipse JDT Test Harness\n" + |
| "Class-Path: lib1.jar\n" + |
| "Class-Path: lib3.jar\n", |
| }, |
| LIB_DIR + "/lib9.jar", |
| JavaCore.VERSION_1_4); |
| Util.createJar( |
| new String[] { |
| "p/A.java", |
| "package p;\n" + |
| "public class A {\n" + |
| "}", |
| }, |
| // spoiled jar: MANIFEST.MF is a directory |
| new String[] { |
| "META-INF/MANIFEST.MF/MANIFEST.MF", |
| "Manifest-Version: 1.0\n" + |
| "Created-By: Eclipse JDT Test Harness\n" + |
| "Class-Path: lib2.jar\n", |
| }, |
| LIB_DIR + "/lib10.jar", |
| JavaCore.VERSION_1_4); |
| Util.createJar( |
| new String[] { |
| "p/A.java", |
| "package p;\n" + |
| "public class A {\n" + |
| "}", |
| }, |
| new String[] { |
| "META-INF/MANIFEST.MF", |
| "Manifest-Version: 1.0\n" + |
| "Created-By: Eclipse JDT Test Harness\n" + |
| "Class-Path:\n", |
| }, |
| LIB_DIR + "/lib11.jar", |
| JavaCore.VERSION_1_4); |
| Util.createJar( |
| null, |
| new String[] { |
| "META-INF/MANIFEST.MF", |
| "Manifest-Version: 1.0\n" + |
| "Created-By: Eclipse JDT Test Harness\n" + |
| "Class-Path:lib1.jar\n", // missing space |
| }, |
| LIB_DIR + "/lib12.jar", |
| JavaCore.VERSION_1_4); |
| Util.createJar( |
| null, |
| new String[] { |
| "META-INF/MANIFEST.MF", |
| "Manifest-Version: 1.0\n" + |
| "Created-By: Eclipse JDT Test Harness\n" + |
| "Class-Path:lib1.jar lib1.jar\n", // missing space |
| }, |
| LIB_DIR + "/lib13.jar", |
| JavaCore.VERSION_1_4); |
| Util.createJar( |
| null, |
| new String[] { |
| "META-INF/MANIFEST.MF", |
| "Manifest-Version: 1.0\n" + |
| "Created-By: Eclipse JDT Test Harness\n" + |
| " Class-Path: lib1.jar\n", // extra space at line start |
| }, |
| LIB_DIR + "/lib14.jar", |
| JavaCore.VERSION_1_4); |
| Util.createJar( |
| null, |
| new String[] { |
| "META-INF/MANIFEST.MF", |
| "Manifest-Version: 1.0\n" + |
| "Created-By: Eclipse JDT Test Harness\n" + |
| "Class-Path: lib1.jar", // missing newline at end |
| }, |
| LIB_DIR + "/lib15.jar", |
| JavaCore.VERSION_1_4); |
| Util.createJar( |
| new String[] { |
| "p/A.java", |
| "package p;\n" + |
| "public class A {\n" + |
| "}", |
| }, |
| new String[] { |
| "META-INF/MANIFEST.MF", |
| "Manifest-Version: 1.0\n" + |
| "Created-By: Eclipse JDT Test Harness\n" + |
| "Class-Path: \n" + |
| " lib2.jar\n", |
| "p/S1.java", |
| "package p;\n" + |
| "public class S1 {\n" + |
| "}", |
| }, |
| LIB_DIR + "/lib16.jar", |
| JavaCore.VERSION_1_4); |
| new File(LIB_DIR + "/dir").mkdir(); |
| Util.createJar( |
| new String[] { |
| "p/A.java", |
| "package p;\n" + |
| "public class A {\n" + |
| "}", |
| }, |
| new String[] { |
| "META-INF/MANIFEST.MF", |
| "Manifest-Version: 1.0\n" + |
| "Created-By: Eclipse JDT Test Harness\n" + |
| "Class-Path: ../lib2.jar\n", |
| }, |
| LIB_DIR + "/dir/lib17.jar", |
| JavaCore.VERSION_1_4); |
| CASCADED_JARS_CREATED = true; |
| } catch (IOException e) { |
| // ignore |
| } |
| } |
| } |
| |
| protected String getLibraryClassesAsQuotedString() { |
| String[] paths = Util.getJavaClassLibs(); |
| StringBuffer buffer = new StringBuffer(); |
| buffer.append('"'); |
| for (int i = 0, max = paths.length; i < max; i++) { |
| if (i != 0) { |
| buffer.append(File.pathSeparatorChar); |
| } |
| buffer.append(paths[i]); |
| } |
| buffer.append('"'); |
| return String.valueOf(buffer); |
| } |
| |
| protected String getJCEJarAsQuotedString() { |
| if (Util.isMacOS()) { |
| return "\"" + JRE_HOME_DIR + "/../Classes/jce.jar\""; |
| } |
| return "\"" + JRE_HOME_DIR + "/lib/jce.jar\""; |
| } |
| |
| protected String getExtDirectory() { |
| return JRE_HOME_DIR + "/lib/ext"; |
| } |
| |
| /** |
| * Run a compilation test that is expected to complete successfully and |
| * compare the outputs to expected ones. |
| * |
| * @param testFiles |
| * the source files, given as a suite of file name, file content; |
| * file names are relative to the output directory |
| * @param commandLine |
| * the command line to pass to |
| * {@link BatchCompiler#compile(String, PrintWriter, PrintWriter, org.eclipse.jdt.core.compiler.CompilationProgress) BatchCompiler#compile} |
| * @param expectedSuccessOutOutputString |
| * the expected contents of the standard output stream; pass null |
| * to bypass the comparison |
| * @param expectedSuccessErrOutputString |
| * the expected contents of the standard error output stream; |
| * pass null to bypass the comparison |
| * @param shouldFlushOutputDirectory |
| * pass true to get the output directory flushed before the test |
| * runs |
| */ |
| protected void runConformTest(String[] testFiles, String commandLine, String expectedSuccessOutOutputString, String expectedSuccessErrOutputString, boolean shouldFlushOutputDirectory) { |
| runTest(true, testFiles, commandLine, expectedSuccessOutOutputString, |
| expectedSuccessErrOutputString, shouldFlushOutputDirectory, null/*progress*/); |
| } |
| |
| /** |
| * Run a compilation test that is expected to fail and compare the outputs |
| * to expected ones. |
| * |
| * @param testFiles |
| * the source files, given as a suite of file name, file content; |
| * file names are relative to the output directory |
| * @param commandLine |
| * the command line to pass to |
| * {@link BatchCompiler#compile(String, PrintWriter, PrintWriter, org.eclipse.jdt.core.compiler.CompilationProgress) BatchCompiler#compile} |
| * @param expectedFailureOutOutputString |
| * the expected contents of the standard output stream; pass null |
| * to bypass the comparison |
| * @param expectedFailureErrOutputString |
| * the expected contents of the standard error output stream; |
| * pass null to bypass the comparison |
| * @param shouldFlushOutputDirectory |
| * pass true to get the output directory flushed before the test |
| * runs |
| */ |
| protected void runNegativeTest(String[] testFiles, String commandLine, String expectedFailureOutOutputString, String expectedFailureErrOutputString, boolean shouldFlushOutputDirectory) { |
| runTest(false, testFiles, commandLine, expectedFailureOutOutputString, |
| expectedFailureErrOutputString, shouldFlushOutputDirectory, null/*progress*/); |
| } |
| |
| protected void runProgressTest(String[] testFiles, String commandLine, String expectedOutOutputString, String expectedErrOutputString, String expectedProgress) { |
| runTest(true/*shouldCompileOK*/, testFiles, commandLine, expectedOutOutputString, expectedErrOutputString, true/*shouldFlushOutputDirectory*/, new TestCompilationProgress()); |
| } |
| |
| protected void runProgressTest(boolean shouldCompileOK, String[] testFiles, String commandLine, String expectedOutOutputString, String expectedErrOutputString, TestCompilationProgress progress, String expectedProgress) { |
| runTest(shouldCompileOK, testFiles, commandLine, expectedOutOutputString, expectedErrOutputString, true/*shouldFlushOutputDirectory*/, progress); |
| String actualProgress = progress.toString(); |
| if (!semiNormalizedComparison(expectedProgress, actualProgress, outputDirNormalizer)) { |
| System.out.println(Util.displayString(outputDirNormalizer.normalized(actualProgress), 2)); |
| assertEquals( |
| "Unexpected progress", |
| expectedProgress, |
| actualProgress); |
| } |
| } |
| |
| /** |
| * Worker method for runConformTest and runNegativeTest. |
| * |
| * @param shouldCompileOK |
| * set to true if the compiler should compile the given sources |
| * without errors |
| * @param testFiles |
| * the source files, given as a suite of file name, file content; |
| * file names are relative to the output directory |
| * @param extraArguments |
| * the command line to pass to {@link Main#compile(String[]) |
| * Main#compile} or other arguments to pass to {@link |
| * #invokeCompiler(PrintWriter, PrintWriter, Object, |
| * BatchCompilerTest.TestCompilationProgress)} (for use |
| * by extending test classes) |
| * @param expectedOutOutputString |
| * the expected contents of the standard output stream; pass null |
| * to bypass the comparison |
| * @param expectedErrOutputString |
| * the expected contents of the standard error output stream; |
| * pass null to bypass the comparison |
| * @param shouldFlushOutputDirectory |
| * pass true to get the output directory flushed before the test |
| * runs |
| */ |
| protected void runTest(boolean shouldCompileOK, String[] testFiles, Object extraArguments, String expectedOutOutputString, String expectedErrOutputString, boolean shouldFlushOutputDirectory, TestCompilationProgress progress) { |
| File outputDirectory = new File(OUTPUT_DIR); |
| if (shouldFlushOutputDirectory) |
| Util.flushDirectoryContent(outputDirectory); |
| try { |
| if (!outputDirectory.isDirectory()) { |
| outputDirectory.mkdirs(); |
| } |
| if (testFiles != null) { |
| PrintWriter sourceFileWriter; |
| for (int i = 0; i < testFiles.length; i += 2) { |
| String fileName = OUTPUT_DIR + File.separator + testFiles[i]; |
| File file = new File(fileName), innerOutputDirectory = file |
| .getParentFile(); |
| if (!innerOutputDirectory.isDirectory()) { |
| innerOutputDirectory.mkdirs(); |
| } |
| sourceFileWriter = new PrintWriter(new FileOutputStream(file)); |
| try { |
| sourceFileWriter.write(testFiles[i + 1]); |
| } finally { |
| sourceFileWriter.close(); |
| } |
| } |
| } |
| } catch (FileNotFoundException e) { |
| e.printStackTrace(); |
| throw new RuntimeException(e); |
| } |
| String printerWritersNameRoot = OUTPUT_DIR + File.separator + testName(); |
| String outFileName = printerWritersNameRoot + "out.txt", |
| errFileName = printerWritersNameRoot + "err.txt"; |
| PrintWriter out = null; |
| PrintWriter err = null; |
| boolean compileOK; |
| try { |
| try { |
| out = new PrintWriter(new FileOutputStream(outFileName)); |
| err = new PrintWriter(new FileOutputStream(errFileName)); |
| } catch (FileNotFoundException e) { |
| System.out.println(getClass().getName() + '#' + getName()); |
| e.printStackTrace(); |
| throw new RuntimeException(e); |
| } |
| compileOK = invokeCompiler(out, err, extraArguments, progress); |
| } finally { |
| if (out != null) |
| out.close(); |
| if (err != null) |
| err.close(); |
| } |
| String outOutputString = Util.fileContent(outFileName), |
| errOutputString = Util.fileContent(errFileName); |
| boolean compareOK = false, outCompareOK = false, errCompareOK = false; |
| if (compileOK == shouldCompileOK) { |
| compareOK = |
| (outCompareOK = semiNormalizedComparison(expectedOutOutputString, |
| outOutputString, outputDirNormalizer)) |
| && (errCompareOK = semiNormalizedComparison(expectedErrOutputString, |
| errOutputString, outputDirNormalizer)); |
| } |
| // test sanity of the test definition: did we forget to use "---OUTPUT_DIR_PLACEHOLDER---" instead of OUTPUT_DIR? |
| if (!outCompareOK) { |
| assertEquals("outputDirNormalizer should not affect expectedOutOutput", expectedOutOutputString, outputDirNormalizer.normalized(expectedOutOutputString)); |
| } |
| if (!errCompareOK) { |
| assertEquals("outputDirNormalizer should not affect expectedErrOutput", expectedErrOutputString, outputDirNormalizer.normalized(expectedErrOutputString)); |
| } |
| if (compileOK != shouldCompileOK || !compareOK) { |
| System.out.println(getClass().getName() + '#' + getName()); |
| if (testFiles != null) { |
| for (int i = 0; i < testFiles.length; i += 2) { |
| System.out.print(testFiles[i]); |
| System.out.println(" ["); |
| System.out.println(testFiles[i + 1]); |
| System.out.println("]"); |
| } |
| } |
| } |
| if (compileOK != shouldCompileOK) |
| System.out.println(errOutputString); |
| if (compileOK == shouldCompileOK && !compareOK) { |
| System.out.println( |
| "------------ [START OUT] ------------\n" |
| + "------------- Expected: -------------\n" |
| + expectedOutOutputString |
| + "\n------------- but was: -------------\n" |
| + outOutputString |
| + "\n--------- (cut and paste:) ----------\n" |
| + Util.displayString(outputDirNormalizer |
| .normalized(outOutputString)) |
| + "\n------------- [END OUT] -------------\n" |
| + "------------ [START ERR] ------------\n" |
| + "------------- Expected: -------------\n" |
| + expectedErrOutputString |
| + "\n------------- but was: -------------\n" |
| + errOutputString |
| + "\n--------- (cut and paste:) ----------\n" |
| + Util.displayString(outputDirNormalizer |
| .normalized(errOutputString)) |
| + "\n------------- [END ERR] -------------\n"); |
| } |
| if (shouldCompileOK) |
| assertTrue("Unexpected problems [out: " + outOutputString + "][err: " + errOutputString + "]", compileOK); |
| else |
| assertFalse("Unexpected success: [out: " + outOutputString + "][err: " + errOutputString + "]", compileOK); |
| if (!outCompareOK) { |
| // calling assertEquals to benefit from the comparison UI |
| // (need appropriate exception) |
| assertEquals( |
| "Unexpected standard output for invocation with arguments [" |
| + extraArguments + "]", |
| expectedOutOutputString, |
| outOutputString); |
| } |
| if (!errCompareOK) { |
| assertEquals( |
| "Unexpected error output for invocation with arguments [" |
| + extraArguments + "]", |
| expectedErrOutputString, |
| errOutputString); |
| } |
| } |
| |
| protected boolean invokeCompiler(PrintWriter out, PrintWriter err, Object extraArguments, TestCompilationProgress compilationProgress) { |
| try { |
| final String[] tokenizedCommandLine = Main.tokenize((String) extraArguments); |
| return new Main(out, err, false, null /* customDefaultOptions */, compilationProgress /* compilationProgress*/).compile(tokenizedCommandLine); |
| } catch (RuntimeException e) { |
| System.out.println(getClass().getName() + '#' + getName()); |
| e.printStackTrace(); |
| throw e; |
| } |
| } |
| |
| protected void runTest(boolean shouldCompileOK, String[] testFiles, String commandLine, Matcher outOutputStringMatcher, Matcher errOutputStringMatcher, boolean shouldFlushOutputDirectory) { |
| File outputDirectory = new File(OUTPUT_DIR); |
| if (shouldFlushOutputDirectory) |
| Util.flushDirectoryContent(outputDirectory); |
| try { |
| if (!outputDirectory.isDirectory()) { |
| outputDirectory.mkdirs(); |
| } |
| PrintWriter sourceFileWriter; |
| for (int i = 0; i < testFiles.length; i += 2) { |
| String fileName = OUTPUT_DIR + File.separator + testFiles[i]; |
| File file = new File(fileName), innerOutputDirectory = file |
| .getParentFile(); |
| if (!innerOutputDirectory.isDirectory()) { |
| innerOutputDirectory.mkdirs(); |
| } |
| sourceFileWriter = new PrintWriter(new FileOutputStream(file)); |
| try { |
| sourceFileWriter.write(testFiles[i + 1]); |
| } finally { |
| sourceFileWriter.close(); |
| } |
| } |
| } catch (FileNotFoundException e) { |
| e.printStackTrace(); |
| throw new RuntimeException(e); |
| } |
| String printerWritersNameRoot = OUTPUT_DIR + File.separator + testName(); |
| String outFileName = printerWritersNameRoot + "out.txt", |
| errFileName = printerWritersNameRoot + "err.txt"; |
| Main batchCompiler; |
| PrintWriter out = null; |
| PrintWriter err = null; |
| boolean compileOK; |
| try { |
| try { |
| out = new PrintWriter(new FileOutputStream(outFileName)); |
| err = new PrintWriter(new FileOutputStream(errFileName)); |
| batchCompiler = new Main(out, err, false/*systemExit*/, null/*options*/, null/*progress*/); |
| } catch (FileNotFoundException e) { |
| System.out.println(getClass().getName() + '#' + getName()); |
| e.printStackTrace(); |
| throw new RuntimeException(e); |
| } |
| try { |
| final String[] tokenizeCommandLine = Main.tokenize(commandLine); |
| compileOK = batchCompiler.compile(tokenizeCommandLine); |
| } catch (RuntimeException e) { |
| compileOK = false; |
| System.out.println(getClass().getName() + '#' + getName()); |
| e.printStackTrace(); |
| throw e; |
| } |
| } finally { |
| if (out != null) |
| out.close(); |
| if (err != null) |
| err.close(); |
| } |
| String outOutputString = Util.fileContent(outFileName), |
| errOutputString = Util.fileContent(errFileName); |
| boolean compareOK = false, outCompareOK = false, errCompareOK = false; |
| String expectedErrOutputString = null, expectedOutOutputString = null; |
| if (compileOK == shouldCompileOK) { |
| if (outOutputStringMatcher == null) { |
| outCompareOK = true; |
| } else { |
| outCompareOK = outOutputStringMatcher.match(outOutputString); |
| expectedOutOutputString = outOutputStringMatcher.expected(); |
| } |
| if (errOutputStringMatcher == null) { |
| errCompareOK = true; |
| } else { |
| errCompareOK = errOutputStringMatcher.match(errOutputString); |
| expectedErrOutputString = errOutputStringMatcher.expected(); |
| } |
| compareOK = outCompareOK && errCompareOK; |
| } |
| if (compileOK != shouldCompileOK || !compareOK) { |
| System.out.println(getClass().getName() + '#' + getName()); |
| for (int i = 0; i < testFiles.length; i += 2) { |
| System.out.print(testFiles[i]); |
| System.out.println(" ["); |
| System.out.println(testFiles[i + 1]); |
| System.out.println("]"); |
| } |
| } |
| if (compileOK != shouldCompileOK) |
| System.out.println(errOutputString); |
| if (compileOK == shouldCompileOK && !compareOK) { |
| System.out.println( |
| "------------ [START OUT] ------------\n" |
| + "------------- Expected: -------------\n" |
| + expectedOutOutputString |
| + "\n------------- but was: -------------\n" |
| + outOutputString |
| + "\n--------- (cut and paste:) ----------\n" |
| + Util.displayString(outputDirNormalizer |
| .normalized(outOutputString)) |
| + "\n------------- [END OUT] -------------\n" |
| + "------------ [START ERR] ------------\n" |
| + "------------- Expected: -------------\n" |
| + expectedErrOutputString |
| + "\n------------- but was: -------------\n" |
| + errOutputString |
| + "\n--------- (cut and paste:) ----------\n" |
| + Util.displayString(outputDirNormalizer |
| .normalized(errOutputString)) |
| + "\n------------- [END ERR] -------------\n"); |
| } |
| if (shouldCompileOK) |
| assertTrue("Unexpected problems: " + errOutputString, compileOK); |
| else |
| assertTrue("Unexpected success: " + errOutputString, !compileOK); |
| if (!outCompareOK) { |
| // calling assertEquals to benefit from the comparison UI |
| // (need appropriate exception) |
| assertEquals( |
| "Unexpected standard output for invocation with arguments [" |
| + commandLine + "]", |
| expectedOutOutputString, |
| outOutputString); |
| } |
| if (!errCompareOK) { |
| assertEquals( |
| "Unexpected error output for invocation with arguments [" |
| + commandLine + "]", |
| expectedErrOutputString, |
| errOutputString); |
| } |
| } |
| |
| protected void runClasspathTest(String classpathInput, String[] expectedClasspathEntries, String expectedError) { |
| File outputDirectory = new File(OUTPUT_DIR); |
| if (!outputDirectory.isDirectory()) { |
| outputDirectory.mkdirs(); |
| } |
| ArrayList<FileSystem.Classpath> paths = new ArrayList<>(Main.DEFAULT_SIZE_CLASSPATH); |
| try { |
| (new Main(new PrintWriter(System.out), new PrintWriter(System.err), true/*systemExit*/, null/*options*/, null/*progress*/)). |
| processPathEntries(Main.DEFAULT_SIZE_CLASSPATH, paths, classpathInput, null /* customEncoding */, true /* isSourceOnly */, false /* rejectDestinationPathOnJars*/); |
| } catch (IllegalArgumentException e) { |
| // e.printStackTrace(); |
| if (expectedError == null) { |
| fail("unexpected invalid input exception: " + e.getMessage()); |
| } else if (! expectedError.equals(e.getMessage())) { |
| System.out.println("\"" + e.getMessage() + "\""); |
| assertEquals(expectedError, e.getMessage()); |
| } |
| return; |
| } |
| if (expectedError == null) { |
| int l = paths.size(); |
| assertEquals("unexpected classpaths entries number: ", |
| expectedClasspathEntries == null ? 0 : expectedClasspathEntries.length / 3, l); |
| for (int i = 0, j = 0; i < l ; i++) { |
| ClasspathLocation result = (ClasspathLocation) paths.get(i); |
| String expected = expectedClasspathEntries[j++]; |
| String actual = result.toString(); |
| if (! actual.equals("ClasspathDirectory " + expected + File.separator) && |
| ! actual.equals("Classpath for jar file " + expected)) { |
| assertEquals("dir/jar " + expected, actual); |
| } |
| expected = expectedClasspathEntries[j++]; |
| if (result.accessRuleSet == null) { |
| assertNull("actual access rule is null instead of <" + expected +">", expected); |
| } else if (! result.accessRuleSet.toString(false). |
| startsWith("AccessRuleSet " + expected)) { |
| System.out.println("\"" + result.accessRuleSet.toString(false) + "\""); |
| fail("inappropriate rules (expected " + expected + |
| ", got " + result.accessRuleSet.toString(false)); |
| } |
| expected = expectedClasspathEntries[j++]; |
| if (expected == null) { |
| assertNull(result.destinationPath); |
| } else if (expected == Main.NONE && |
| result.destinationPath != Main.NONE) { |
| fail("expected 'none' output directory"); |
| } else if (! expected.equals(result.destinationPath)) { |
| System.out.println("\"" + result.destinationPath + "\""); |
| assertEquals(expected, result.destinationPath); |
| } |
| } |
| } else { |
| fail("missing error: " + expectedError); |
| } |
| } |
| |
| /** |
| * Check that no line of message extends beyond width columns. Tabs count for |
| * 4 characters. |
| * @param message the message to check |
| * @param width the maximum number of columns for the message |
| */ |
| protected void checkWidth(String message, int width) { |
| BufferedReader reader = new BufferedReader( |
| new StringReader(message.replaceAll("\t", " "))); |
| String line; |
| try { |
| while ((line = reader.readLine()) != null) { |
| assertTrue("line exceeds " + width + "characters: " + line, |
| line.length() <= width); |
| } |
| } catch (IOException e) { |
| // should never happen on a StringReader |
| } |
| } |
| |
| private static boolean equals(String a, String b) { |
| StringBuffer aBuffer = new StringBuffer(a), bBuffer = new StringBuffer(b); |
| int length = aBuffer.length(), bLength; |
| boolean result = true; |
| if (length != (bLength = bBuffer.length())) { |
| System.err.println("a and b lengths differ"); |
| if (length > bLength) { |
| length = bLength; |
| } |
| result = false; |
| } |
| for (int i = 0; i < length; i++) |
| if (aBuffer.charAt(i) != bBuffer.charAt(i)) { |
| int beforeStart = i - 5, beforeEnd = i - 1, afterStart = i + 1, afterEnd = i + 5; |
| if (beforeStart < 0) { |
| beforeStart = 0; |
| if (beforeEnd < 0) |
| beforeEnd = 0; |
| } |
| if (afterEnd >= length) { |
| afterEnd = length - 1; |
| if (afterStart >= length) |
| afterStart = length - 1; |
| } |
| System.err.println("a and b differ at rank: " + i |
| + "\na: ..." + aBuffer.substring(beforeStart, beforeEnd) |
| + "<" + aBuffer.charAt(i) + ">" |
| + aBuffer.substring(afterStart, afterEnd) + "..." |
| + "\nb: ..." + bBuffer.substring(beforeStart, beforeEnd) |
| + "<" + bBuffer.charAt(i) + ">" |
| + bBuffer.substring(afterStart, afterEnd) + "..."); |
| return false; |
| } |
| return result; // may be false if one of the strings equals the beginning |
| // of the other one, which is longer anyway |
| } |
| |
| /** |
| * Return true if and only if the two strings passed as parameters compare |
| * equal, modulo the transformation of the second string by a normalizer |
| * passed in parameter. This is meant to erase the variations of subparts of |
| * the compared strings in function of the test machine, the user account, |
| * etc. |
| * |
| * @param keep |
| * the first string to compare, gets compared as it is |
| * @param normalize |
| * the second string to compare, passed through the normalizer |
| * before comparison |
| * @param normalizer |
| * the transformation applied to normalize |
| * @return true if keep and normalize compare equal after normalize has been |
| * normalized |
| */ |
| protected boolean semiNormalizedComparison(String keep, String normalize, Normalizer normalizer) { |
| if (keep == null) |
| return normalize == null; |
| if (normalize == null) |
| return false; |
| // return keep.equals(normalizer.normalized(normalize)); |
| return equals(keep, normalizer.normalized(normalize)); |
| } |
| |
| } |