blob: 6a3314bdf4632b1a8bd8c2db25683d38c7a4e614 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2017 GK Software AG and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* 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.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
}
/**
* 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;
}
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));
}
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<ClasspathLocation> 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 = 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));
}
}