1 /******************************************************************************* 2 * Copyright (c) 2017 GK Software AG and others. 3 * 4 * This program and the accompanying materials 5 * are made available under the terms of the Eclipse Public License 2.0 6 * which accompanies this distribution, and is available at 7 * https://www.eclipse.org/legal/epl-2.0/ 8 * 9 * SPDX-License-Identifier: EPL-2.0 10 * 11 * Contributors: 12 * Stephan Herrmann - initial API and implementation 13 *******************************************************************************/ 14 package org.eclipse.jdt.core.tests.compiler.regression; 15 16 import java.io.BufferedReader; 17 import java.io.File; 18 import java.io.FileNotFoundException; 19 import java.io.FileOutputStream; 20 import java.io.IOException; 21 import java.io.PrintWriter; 22 import java.io.StringReader; 23 import java.util.ArrayList; 24 25 import org.eclipse.jdt.core.JavaCore; 26 import org.eclipse.jdt.core.compiler.CompilationProgress; 27 import org.eclipse.jdt.core.compiler.batch.BatchCompiler; 28 import org.eclipse.jdt.core.tests.util.Util; 29 import org.eclipse.jdt.internal.compiler.batch.ClasspathLocation; 30 import org.eclipse.jdt.internal.compiler.batch.FileSystem; 31 import org.eclipse.jdt.internal.compiler.batch.Main; 32 33 public abstract class AbstractBatchCompilerTest extends AbstractRegressionTest { 34 35 protected static abstract class Matcher { match(String effective)36 abstract boolean match(String effective); expected()37 abstract String expected(); // for use in JUnit comparison framework 38 } 39 40 /** 41 * Abstract normalizer for output comparison. This class merely embodies a 42 * chain of responsibility, plus the signature of the method of interest 43 * here, that is {@link #normalized(String) normalized}. 44 */ 45 protected static abstract class Normalizer { 46 private Normalizer nextInChain; Normalizer(Normalizer nextInChain)47 Normalizer(Normalizer nextInChain) { 48 this.nextInChain = nextInChain; 49 } normalized(String originalValue)50 String normalized(String originalValue) { 51 String result; 52 if (this.nextInChain == null) 53 result = Util.convertToIndependantLineDelimiter(originalValue); 54 else 55 result = this.nextInChain.normalized(originalValue); 56 return result; 57 } 58 } 59 60 /** 61 * This normalizer replaces occurrences of a given string with a given 62 * placeholder. 63 */ 64 protected static class StringNormalizer extends Normalizer { 65 private String match; 66 private int matchLength; 67 private String placeholder; StringNormalizer(Normalizer nextInChain, String match, String placeholder)68 StringNormalizer(Normalizer nextInChain, String match, String placeholder) { 69 super(nextInChain); 70 this.match = match; 71 this.matchLength = match.length(); 72 this.placeholder = placeholder; 73 } 74 @Override normalized(String originalValue)75 String normalized(String originalValue) { 76 String result; 77 StringBuffer normalizedValueBuffer = new StringBuffer(originalValue); 78 int nextOccurrenceIndex; 79 while ((nextOccurrenceIndex = normalizedValueBuffer.indexOf(this.match)) != -1) 80 normalizedValueBuffer.replace(nextOccurrenceIndex, 81 nextOccurrenceIndex + this.matchLength, this.placeholder); 82 result = super.normalized(normalizedValueBuffer.toString()); 83 return result; 84 } 85 } 86 87 protected static class TestCompilationProgress extends CompilationProgress { 88 boolean isCanceled = false; 89 int workedSoFar = 0; 90 StringBuffer buffer = new StringBuffer(); begin(int remainingWork)91 public void begin(int remainingWork) { 92 this.buffer.append("----------\n[worked: 0 - remaining: ").append(remainingWork).append("]\n"); 93 } done()94 public void done() { 95 this.buffer.append("----------\n"); 96 } isCanceled()97 public boolean isCanceled() { 98 return this.isCanceled; 99 } setTaskName(String name)100 public void setTaskName(String name) { 101 this.buffer.append(name).append('\n'); 102 } toString()103 public String toString() { 104 return this.buffer.toString(); 105 } worked(int workIncrement, int remainingWork)106 public void worked(int workIncrement, int remainingWork) { 107 this.workedSoFar += workIncrement; 108 this.buffer.append("[worked: ").append(this.workedSoFar).append(" - remaining: ").append(remainingWork).append("]\n"); 109 } 110 } 111 112 public static final String OUTPUT_DIR_PLACEHOLDER = "---OUTPUT_DIR_PLACEHOLDER---"; 113 public static final String LIB_DIR_PLACEHOLDER = "---LIB_DIR_PLACEHOLDER---"; 114 115 /** 116 * Normalizer instance that replaces occurrences of OUTPUT_DIR with 117 * OUTPUT_DIR_PLACEHOLDER and changes file separator to / if the 118 * platform file separator is different from /. 119 */ 120 protected static Normalizer outputDirNormalizer; 121 static { 122 if (File.separatorChar == '/') { 123 outputDirNormalizer = 124 new StringNormalizer( 125 new StringNormalizer( 126 null, OUTPUT_DIR, OUTPUT_DIR_PLACEHOLDER), 127 LIB_DIR, LIB_DIR_PLACEHOLDER); 128 } 129 else { 130 outputDirNormalizer = 131 new StringNormalizer( 132 new StringNormalizer( 133 new StringNormalizer( 134 null, File.separator, "/"), 135 OUTPUT_DIR, OUTPUT_DIR_PLACEHOLDER), 136 LIB_DIR, LIB_DIR_PLACEHOLDER); 137 } 138 } 139 AbstractBatchCompilerTest(String name)140 public AbstractBatchCompilerTest(String name) { 141 super(name); 142 } 143 144 protected static final String JRE_HOME_DIR = Util.getJREDirectory(); 145 protected static final Main MAIN = new Main(null/*outWriter*/, null/*errWriter*/, false/*systemExit*/, null/*options*/, null/*progress*/); 146 147 private static boolean CASCADED_JARS_CREATED; 148 @Override setUp()149 protected void setUp() throws Exception { 150 super.setUp(); 151 CASCADED_JARS_CREATED = false; // initialization needed for each subclass individually 152 } 153 createCascadedJars()154 protected void createCascadedJars() { 155 if (!CASCADED_JARS_CREATED) { 156 File libDir = new File(LIB_DIR); 157 Util.delete(libDir); // make sure we recycle the libs 158 libDir.mkdirs(); 159 try { 160 Util.createJar( 161 new String[] { 162 "p/A.java", 163 "package p;\n" + 164 "public class A {\n" + 165 "}", 166 }, 167 new String[] { 168 "META-INF/MANIFEST.MF", 169 "Manifest-Version: 1.0\n" + 170 "Created-By: Eclipse JDT Test Harness\n" + 171 "Class-Path: lib2.jar\n", 172 "p/S1.java", 173 "package p;\n" + 174 "public class S1 {\n" + 175 "}", 176 }, 177 LIB_DIR + "/lib1.jar", 178 JavaCore.VERSION_1_4); 179 Util.createJar( 180 new String[] { 181 "p/B.java", 182 "package p;\n" + 183 "public class B {\n" + 184 "}", 185 "p/R.java", 186 "package p;\n" + 187 "public class R {\n" + 188 " public static final int R2 = 2;\n" + 189 "}", 190 }, 191 new String[] { 192 "p/S2.java", 193 "package p;\n" + 194 "public class S2 {\n" + 195 "}", 196 }, 197 LIB_DIR + "/lib2.jar", 198 JavaCore.VERSION_1_4); 199 Util.createJar( 200 new String[] { 201 "p/C.java", 202 "package p;\n" + 203 "public class C {\n" + 204 "}", 205 "p/R.java", 206 "package p;\n" + 207 "public class R {\n" + 208 " public static final int R3 = 3;\n" + 209 "}", 210 }, 211 new String[] { 212 "META-INF/MANIFEST.MF", 213 "Manifest-Version: 1.0\n" + 214 "Created-By: Eclipse JDT Test Harness\n" + 215 "Class-Path: lib4.jar\n", 216 }, 217 LIB_DIR + "/lib3.jar", 218 JavaCore.VERSION_1_4); 219 Util.createJar( 220 new String[] { 221 "p/D.java", 222 "package p;\n" + 223 "public class D {\n" + 224 "}", 225 }, 226 new String[] { 227 "META-INF/MANIFEST.MF", 228 "Manifest-Version: 1.0\n" + 229 "Created-By: Eclipse JDT Test Harness\n" + 230 "Class-Path: lib1.jar lib3.jar\n", 231 }, 232 LIB_DIR + "/lib4.jar", 233 JavaCore.VERSION_1_4); 234 Util.createJar( 235 new String[] { 236 "p/C.java", 237 "package p;\n" + 238 "public class C {\n" + 239 "}", 240 "p/R.java", 241 "package p;\n" + 242 "public class R {\n" + 243 " public static final int R3 = 3;\n" + 244 "}", 245 }, 246 new String[] { 247 "META-INF/MANIFEST.MF", 248 "Manifest-Version: 1.0\n" + 249 "Created-By: Eclipse JDT Test Harness\n" + 250 "Class-Path: s/lib6.jar\n", 251 }, 252 LIB_DIR + "/lib5.jar", 253 JavaCore.VERSION_1_4); 254 new File(LIB_DIR + "/s").mkdir(); 255 Util.createJar( 256 new String[] { 257 "p/D.java", 258 "package p;\n" + 259 "public class D {\n" + 260 "}", 261 }, 262 new String[] { 263 "META-INF/MANIFEST.MF", 264 "Manifest-Version: 1.0\n" + 265 "Created-By: Eclipse JDT Test Harness\n" + 266 "Class-Path: ../lib7.jar\n", 267 }, 268 LIB_DIR + "/s/lib6.jar", 269 JavaCore.VERSION_1_4); 270 Util.createJar( 271 new String[] { 272 "p/A.java", 273 "package p;\n" + 274 "public class A {\n" + 275 "}", 276 }, 277 new String[] { 278 "META-INF/MANIFEST.MF", 279 "Manifest-Version: 1.0\n" + 280 "Created-By: Eclipse JDT Test Harness\n" + 281 "Class-Path: lib2.jar\n", 282 }, 283 LIB_DIR + "/lib7.jar", 284 JavaCore.VERSION_1_4); 285 Util.createJar( 286 new String[] { 287 "p/F.java", 288 "package p;\n" + 289 "public class F {\n" + 290 "}", 291 }, 292 new String[] { 293 "META-INF/MANIFEST.MF", 294 "Manifest-Version: 1.0\n" + 295 "Created-By: Eclipse JDT Test Harness\n" + 296 "Class-Path: " + LIB_DIR + "/lib3.jar lib1.jar\n", 297 }, 298 LIB_DIR + "/lib8.jar", 299 JavaCore.VERSION_1_4); 300 Util.createJar( 301 new String[] { 302 "p/G.java", 303 "package p;\n" + 304 "public class G {\n" + 305 "}", 306 }, 307 new String[] { 308 "META-INF/MANIFEST.MF", 309 "Manifest-Version: 1.0\n" + 310 "Created-By: Eclipse JDT Test Harness\n" + 311 "Class-Path: lib1.jar\n" + 312 "Class-Path: lib3.jar\n", 313 }, 314 LIB_DIR + "/lib9.jar", 315 JavaCore.VERSION_1_4); 316 Util.createJar( 317 new String[] { 318 "p/A.java", 319 "package p;\n" + 320 "public class A {\n" + 321 "}", 322 }, 323 // spoiled jar: MANIFEST.MF is a directory 324 new String[] { 325 "META-INF/MANIFEST.MF/MANIFEST.MF", 326 "Manifest-Version: 1.0\n" + 327 "Created-By: Eclipse JDT Test Harness\n" + 328 "Class-Path: lib2.jar\n", 329 }, 330 LIB_DIR + "/lib10.jar", 331 JavaCore.VERSION_1_4); 332 Util.createJar( 333 new String[] { 334 "p/A.java", 335 "package p;\n" + 336 "public class A {\n" + 337 "}", 338 }, 339 new String[] { 340 "META-INF/MANIFEST.MF", 341 "Manifest-Version: 1.0\n" + 342 "Created-By: Eclipse JDT Test Harness\n" + 343 "Class-Path:\n", 344 }, 345 LIB_DIR + "/lib11.jar", 346 JavaCore.VERSION_1_4); 347 Util.createJar( 348 null, 349 new String[] { 350 "META-INF/MANIFEST.MF", 351 "Manifest-Version: 1.0\n" + 352 "Created-By: Eclipse JDT Test Harness\n" + 353 "Class-Path:lib1.jar\n", // missing space 354 }, 355 LIB_DIR + "/lib12.jar", 356 JavaCore.VERSION_1_4); 357 Util.createJar( 358 null, 359 new String[] { 360 "META-INF/MANIFEST.MF", 361 "Manifest-Version: 1.0\n" + 362 "Created-By: Eclipse JDT Test Harness\n" + 363 "Class-Path:lib1.jar lib1.jar\n", // missing space 364 }, 365 LIB_DIR + "/lib13.jar", 366 JavaCore.VERSION_1_4); 367 Util.createJar( 368 null, 369 new String[] { 370 "META-INF/MANIFEST.MF", 371 "Manifest-Version: 1.0\n" + 372 "Created-By: Eclipse JDT Test Harness\n" + 373 " Class-Path: lib1.jar\n", // extra space at line start 374 }, 375 LIB_DIR + "/lib14.jar", 376 JavaCore.VERSION_1_4); 377 Util.createJar( 378 null, 379 new String[] { 380 "META-INF/MANIFEST.MF", 381 "Manifest-Version: 1.0\n" + 382 "Created-By: Eclipse JDT Test Harness\n" + 383 "Class-Path: lib1.jar", // missing newline at end 384 }, 385 LIB_DIR + "/lib15.jar", 386 JavaCore.VERSION_1_4); 387 Util.createJar( 388 new String[] { 389 "p/A.java", 390 "package p;\n" + 391 "public class A {\n" + 392 "}", 393 }, 394 new String[] { 395 "META-INF/MANIFEST.MF", 396 "Manifest-Version: 1.0\n" + 397 "Created-By: Eclipse JDT Test Harness\n" + 398 "Class-Path: \n" + 399 " lib2.jar\n", 400 "p/S1.java", 401 "package p;\n" + 402 "public class S1 {\n" + 403 "}", 404 }, 405 LIB_DIR + "/lib16.jar", 406 JavaCore.VERSION_1_4); 407 new File(LIB_DIR + "/dir").mkdir(); 408 Util.createJar( 409 new String[] { 410 "p/A.java", 411 "package p;\n" + 412 "public class A {\n" + 413 "}", 414 }, 415 new String[] { 416 "META-INF/MANIFEST.MF", 417 "Manifest-Version: 1.0\n" + 418 "Created-By: Eclipse JDT Test Harness\n" + 419 "Class-Path: ../lib2.jar\n", 420 }, 421 LIB_DIR + "/dir/lib17.jar", 422 JavaCore.VERSION_1_4); 423 CASCADED_JARS_CREATED = true; 424 } catch (IOException e) { 425 // ignore 426 } 427 } 428 } 429 getLibraryClassesAsQuotedString()430 protected String getLibraryClassesAsQuotedString() { 431 String[] paths = Util.getJavaClassLibs(); 432 StringBuffer buffer = new StringBuffer(); 433 buffer.append('"'); 434 for (int i = 0, max = paths.length; i < max; i++) { 435 if (i != 0) { 436 buffer.append(File.pathSeparatorChar); 437 } 438 buffer.append(paths[i]); 439 } 440 buffer.append('"'); 441 return String.valueOf(buffer); 442 } 443 getJCEJarAsQuotedString()444 protected String getJCEJarAsQuotedString() { 445 if (Util.isMacOS()) { 446 return "\"" + JRE_HOME_DIR + "/../Classes/jce.jar\""; 447 } 448 return "\"" + JRE_HOME_DIR + "/lib/jce.jar\""; 449 } 450 getExtDirectory()451 protected String getExtDirectory() { 452 return JRE_HOME_DIR + "/lib/ext"; 453 } 454 455 /** 456 * Run a compilation test that is expected to complete successfully and 457 * compare the outputs to expected ones. 458 * 459 * @param testFiles 460 * the source files, given as a suite of file name, file content; 461 * file names are relative to the output directory 462 * @param commandLine 463 * the command line to pass to 464 * {@link BatchCompiler#compile(String, PrintWriter, PrintWriter, org.eclipse.jdt.core.compiler.CompilationProgress) BatchCompiler#compile} 465 * @param expectedSuccessOutOutputString 466 * the expected contents of the standard output stream; pass null 467 * to bypass the comparison 468 * @param expectedSuccessErrOutputString 469 * the expected contents of the standard error output stream; 470 * pass null to bypass the comparison 471 * @param shouldFlushOutputDirectory 472 * pass true to get the output directory flushed before the test 473 * runs 474 */ runConformTest(String[] testFiles, String commandLine, String expectedSuccessOutOutputString, String expectedSuccessErrOutputString, boolean shouldFlushOutputDirectory)475 protected void runConformTest(String[] testFiles, String commandLine, String expectedSuccessOutOutputString, String expectedSuccessErrOutputString, boolean shouldFlushOutputDirectory) { 476 runTest(true, testFiles, commandLine, expectedSuccessOutOutputString, 477 expectedSuccessErrOutputString, shouldFlushOutputDirectory, null/*progress*/); 478 } 479 480 /** 481 * Run a compilation test that is expected to fail and compare the outputs 482 * to expected ones. 483 * 484 * @param testFiles 485 * the source files, given as a suite of file name, file content; 486 * file names are relative to the output directory 487 * @param commandLine 488 * the command line to pass to 489 * {@link BatchCompiler#compile(String, PrintWriter, PrintWriter, org.eclipse.jdt.core.compiler.CompilationProgress) BatchCompiler#compile} 490 * @param expectedFailureOutOutputString 491 * the expected contents of the standard output stream; pass null 492 * to bypass the comparison 493 * @param expectedFailureErrOutputString 494 * the expected contents of the standard error output stream; 495 * pass null to bypass the comparison 496 * @param shouldFlushOutputDirectory 497 * pass true to get the output directory flushed before the test 498 * runs 499 */ runNegativeTest(String[] testFiles, String commandLine, String expectedFailureOutOutputString, String expectedFailureErrOutputString, boolean shouldFlushOutputDirectory)500 protected void runNegativeTest(String[] testFiles, String commandLine, String expectedFailureOutOutputString, String expectedFailureErrOutputString, boolean shouldFlushOutputDirectory) { 501 runTest(false, testFiles, commandLine, expectedFailureOutOutputString, 502 expectedFailureErrOutputString, shouldFlushOutputDirectory, null/*progress*/); 503 } 504 runProgressTest(String[] testFiles, String commandLine, String expectedOutOutputString, String expectedErrOutputString, String expectedProgress)505 protected void runProgressTest(String[] testFiles, String commandLine, String expectedOutOutputString, String expectedErrOutputString, String expectedProgress) { 506 runTest(true/*shouldCompileOK*/, testFiles, commandLine, expectedOutOutputString, expectedErrOutputString, true/*shouldFlushOutputDirectory*/, new TestCompilationProgress()); 507 } 508 runProgressTest(boolean shouldCompileOK, String[] testFiles, String commandLine, String expectedOutOutputString, String expectedErrOutputString, TestCompilationProgress progress, String expectedProgress)509 protected void runProgressTest(boolean shouldCompileOK, String[] testFiles, String commandLine, String expectedOutOutputString, String expectedErrOutputString, TestCompilationProgress progress, String expectedProgress) { 510 runTest(shouldCompileOK, testFiles, commandLine, expectedOutOutputString, expectedErrOutputString, true/*shouldFlushOutputDirectory*/, progress); 511 String actualProgress = progress.toString(); 512 if (!semiNormalizedComparison(expectedProgress, actualProgress, outputDirNormalizer)) { 513 System.out.println(Util.displayString(outputDirNormalizer.normalized(actualProgress), 2)); 514 assertEquals( 515 "Unexpected progress", 516 expectedProgress, 517 actualProgress); 518 } 519 } 520 521 /** 522 * Worker method for runConformTest and runNegativeTest. 523 * 524 * @param shouldCompileOK 525 * set to true if the compiler should compile the given sources 526 * without errors 527 * @param testFiles 528 * the source files, given as a suite of file name, file content; 529 * file names are relative to the output directory 530 * @param extraArguments 531 * the command line to pass to {@link Main#compile(String[]) 532 * Main#compile} or other arguments to pass to {@link 533 * #invokeCompiler(PrintWriter, PrintWriter, Object, 534 * BatchCompilerTest.TestCompilationProgress)} (for use 535 * by extending test classes) 536 * @param expectedOutOutputString 537 * the expected contents of the standard output stream; pass null 538 * to bypass the comparison 539 * @param expectedErrOutputString 540 * the expected contents of the standard error output stream; 541 * pass null to bypass the comparison 542 * @param shouldFlushOutputDirectory 543 * pass true to get the output directory flushed before the test 544 * runs 545 */ runTest(boolean shouldCompileOK, String[] testFiles, Object extraArguments, String expectedOutOutputString, String expectedErrOutputString, boolean shouldFlushOutputDirectory, TestCompilationProgress progress)546 protected void runTest(boolean shouldCompileOK, String[] testFiles, Object extraArguments, String expectedOutOutputString, String expectedErrOutputString, boolean shouldFlushOutputDirectory, TestCompilationProgress progress) { 547 File outputDirectory = new File(OUTPUT_DIR); 548 if (shouldFlushOutputDirectory) 549 Util.flushDirectoryContent(outputDirectory); 550 try { 551 if (!outputDirectory.isDirectory()) { 552 outputDirectory.mkdirs(); 553 } 554 if (testFiles != null) { 555 PrintWriter sourceFileWriter; 556 for (int i = 0; i < testFiles.length; i += 2) { 557 String fileName = OUTPUT_DIR + File.separator + testFiles[i]; 558 File file = new File(fileName), innerOutputDirectory = file 559 .getParentFile(); 560 if (!innerOutputDirectory.isDirectory()) { 561 innerOutputDirectory.mkdirs(); 562 } 563 sourceFileWriter = new PrintWriter(new FileOutputStream(file)); 564 try { 565 sourceFileWriter.write(testFiles[i + 1]); 566 } finally { 567 sourceFileWriter.close(); 568 } 569 } 570 } 571 } catch (FileNotFoundException e) { 572 e.printStackTrace(); 573 throw new RuntimeException(e); 574 } 575 String printerWritersNameRoot = OUTPUT_DIR + File.separator + testName(); 576 String outFileName = printerWritersNameRoot + "out.txt", 577 errFileName = printerWritersNameRoot + "err.txt"; 578 PrintWriter out = null; 579 PrintWriter err = null; 580 boolean compileOK; 581 try { 582 try { 583 out = new PrintWriter(new FileOutputStream(outFileName)); 584 err = new PrintWriter(new FileOutputStream(errFileName)); 585 } catch (FileNotFoundException e) { 586 System.out.println(getClass().getName() + '#' + getName()); 587 e.printStackTrace(); 588 throw new RuntimeException(e); 589 } 590 compileOK = invokeCompiler(out, err, extraArguments, progress); 591 } finally { 592 if (out != null) 593 out.close(); 594 if (err != null) 595 err.close(); 596 } 597 String outOutputString = Util.fileContent(outFileName), 598 errOutputString = Util.fileContent(errFileName); 599 boolean compareOK = false, outCompareOK = false, errCompareOK = false; 600 if (compileOK == shouldCompileOK) { 601 compareOK = 602 (outCompareOK = semiNormalizedComparison(expectedOutOutputString, 603 outOutputString, outputDirNormalizer)) 604 && (errCompareOK = semiNormalizedComparison(expectedErrOutputString, 605 errOutputString, outputDirNormalizer)); 606 } 607 if (compileOK != shouldCompileOK || !compareOK) { 608 System.out.println(getClass().getName() + '#' + getName()); 609 if (testFiles != null) { 610 for (int i = 0; i < testFiles.length; i += 2) { 611 System.out.print(testFiles[i]); 612 System.out.println(" ["); 613 System.out.println(testFiles[i + 1]); 614 System.out.println("]"); 615 } 616 } 617 } 618 if (compileOK != shouldCompileOK) 619 System.out.println(errOutputString); 620 if (compileOK == shouldCompileOK && !compareOK) { 621 System.out.println( 622 "------------ [START OUT] ------------\n" 623 + "------------- Expected: -------------\n" 624 + expectedOutOutputString 625 + "\n------------- but was: -------------\n" 626 + outOutputString 627 + "\n--------- (cut and paste:) ----------\n" 628 + Util.displayString(outputDirNormalizer 629 .normalized(outOutputString)) 630 + "\n------------- [END OUT] -------------\n" 631 + "------------ [START ERR] ------------\n" 632 + "------------- Expected: -------------\n" 633 + expectedErrOutputString 634 + "\n------------- but was: -------------\n" 635 + errOutputString 636 + "\n--------- (cut and paste:) ----------\n" 637 + Util.displayString(outputDirNormalizer 638 .normalized(errOutputString)) 639 + "\n------------- [END ERR] -------------\n"); 640 } 641 if (shouldCompileOK) 642 assertTrue("Unexpected problems [out: " + outOutputString + "][err: " + errOutputString + "]", compileOK); 643 else 644 assertFalse("Unexpected success: [out: " + outOutputString + "][err: " + errOutputString + "]", compileOK); 645 if (!outCompareOK) { 646 // calling assertEquals to benefit from the comparison UI 647 // (need appropriate exception) 648 assertEquals( 649 "Unexpected standard output for invocation with arguments [" 650 + extraArguments + "]", 651 expectedOutOutputString, 652 outOutputString); 653 } 654 if (!errCompareOK) { 655 assertEquals( 656 "Unexpected error output for invocation with arguments [" 657 + extraArguments + "]", 658 expectedErrOutputString, 659 errOutputString); 660 } 661 } 662 invokeCompiler(PrintWriter out, PrintWriter err, Object extraArguments, TestCompilationProgress compilationProgress)663 protected boolean invokeCompiler(PrintWriter out, PrintWriter err, Object extraArguments, TestCompilationProgress compilationProgress) { 664 try { 665 final String[] tokenizedCommandLine = Main.tokenize((String) extraArguments); 666 return new Main(out, err, false, null /* customDefaultOptions */, compilationProgress /* compilationProgress*/).compile(tokenizedCommandLine); 667 } catch (RuntimeException e) { 668 System.out.println(getClass().getName() + '#' + getName()); 669 e.printStackTrace(); 670 throw e; 671 } 672 } 673 runTest(boolean shouldCompileOK, String[] testFiles, String commandLine, Matcher outOutputStringMatcher, Matcher errOutputStringMatcher, boolean shouldFlushOutputDirectory)674 protected void runTest(boolean shouldCompileOK, String[] testFiles, String commandLine, Matcher outOutputStringMatcher, Matcher errOutputStringMatcher, boolean shouldFlushOutputDirectory) { 675 File outputDirectory = new File(OUTPUT_DIR); 676 if (shouldFlushOutputDirectory) 677 Util.flushDirectoryContent(outputDirectory); 678 try { 679 if (!outputDirectory.isDirectory()) { 680 outputDirectory.mkdirs(); 681 } 682 PrintWriter sourceFileWriter; 683 for (int i = 0; i < testFiles.length; i += 2) { 684 String fileName = OUTPUT_DIR + File.separator + testFiles[i]; 685 File file = new File(fileName), innerOutputDirectory = file 686 .getParentFile(); 687 if (!innerOutputDirectory.isDirectory()) { 688 innerOutputDirectory.mkdirs(); 689 } 690 sourceFileWriter = new PrintWriter(new FileOutputStream(file)); 691 try { 692 sourceFileWriter.write(testFiles[i + 1]); 693 } finally { 694 sourceFileWriter.close(); 695 } 696 } 697 } catch (FileNotFoundException e) { 698 e.printStackTrace(); 699 throw new RuntimeException(e); 700 } 701 String printerWritersNameRoot = OUTPUT_DIR + File.separator + testName(); 702 String outFileName = printerWritersNameRoot + "out.txt", 703 errFileName = printerWritersNameRoot + "err.txt"; 704 Main batchCompiler; 705 PrintWriter out = null; 706 PrintWriter err = null; 707 boolean compileOK; 708 try { 709 try { 710 out = new PrintWriter(new FileOutputStream(outFileName)); 711 err = new PrintWriter(new FileOutputStream(errFileName)); 712 batchCompiler = new Main(out, err, false/*systemExit*/, null/*options*/, null/*progress*/); 713 } catch (FileNotFoundException e) { 714 System.out.println(getClass().getName() + '#' + getName()); 715 e.printStackTrace(); 716 throw new RuntimeException(e); 717 } 718 try { 719 final String[] tokenizeCommandLine = Main.tokenize(commandLine); 720 compileOK = batchCompiler.compile(tokenizeCommandLine); 721 } catch (RuntimeException e) { 722 compileOK = false; 723 System.out.println(getClass().getName() + '#' + getName()); 724 e.printStackTrace(); 725 throw e; 726 } 727 } finally { 728 if (out != null) 729 out.close(); 730 if (err != null) 731 err.close(); 732 } 733 String outOutputString = Util.fileContent(outFileName), 734 errOutputString = Util.fileContent(errFileName); 735 boolean compareOK = false, outCompareOK = false, errCompareOK = false; 736 String expectedErrOutputString = null, expectedOutOutputString = null; 737 if (compileOK == shouldCompileOK) { 738 if (outOutputStringMatcher == null) { 739 outCompareOK = true; 740 } else { 741 outCompareOK = outOutputStringMatcher.match(outOutputString); 742 expectedOutOutputString = outOutputStringMatcher.expected(); 743 } 744 if (errOutputStringMatcher == null) { 745 errCompareOK = true; 746 } else { 747 errCompareOK = errOutputStringMatcher.match(errOutputString); 748 expectedErrOutputString = errOutputStringMatcher.expected(); 749 } 750 compareOK = outCompareOK && errCompareOK; 751 } 752 if (compileOK != shouldCompileOK || !compareOK) { 753 System.out.println(getClass().getName() + '#' + getName()); 754 for (int i = 0; i < testFiles.length; i += 2) { 755 System.out.print(testFiles[i]); 756 System.out.println(" ["); 757 System.out.println(testFiles[i + 1]); 758 System.out.println("]"); 759 } 760 } 761 if (compileOK != shouldCompileOK) 762 System.out.println(errOutputString); 763 if (compileOK == shouldCompileOK && !compareOK) { 764 System.out.println( 765 "------------ [START OUT] ------------\n" 766 + "------------- Expected: -------------\n" 767 + expectedOutOutputString 768 + "\n------------- but was: -------------\n" 769 + outOutputString 770 + "\n--------- (cut and paste:) ----------\n" 771 + Util.displayString(outputDirNormalizer 772 .normalized(outOutputString)) 773 + "\n------------- [END OUT] -------------\n" 774 + "------------ [START ERR] ------------\n" 775 + "------------- Expected: -------------\n" 776 + expectedErrOutputString 777 + "\n------------- but was: -------------\n" 778 + errOutputString 779 + "\n--------- (cut and paste:) ----------\n" 780 + Util.displayString(outputDirNormalizer 781 .normalized(errOutputString)) 782 + "\n------------- [END ERR] -------------\n"); 783 } 784 if (shouldCompileOK) 785 assertTrue("Unexpected problems: " + errOutputString, compileOK); 786 else 787 assertTrue("Unexpected success: " + errOutputString, !compileOK); 788 if (!outCompareOK) { 789 // calling assertEquals to benefit from the comparison UI 790 // (need appropriate exception) 791 assertEquals( 792 "Unexpected standard output for invocation with arguments [" 793 + commandLine + "]", 794 expectedOutOutputString, 795 outOutputString); 796 } 797 if (!errCompareOK) { 798 assertEquals( 799 "Unexpected error output for invocation with arguments [" 800 + commandLine + "]", 801 expectedErrOutputString, 802 errOutputString); 803 } 804 } 805 runClasspathTest(String classpathInput, String[] expectedClasspathEntries, String expectedError)806 protected void runClasspathTest(String classpathInput, String[] expectedClasspathEntries, String expectedError) { 807 File outputDirectory = new File(OUTPUT_DIR); 808 if (!outputDirectory.isDirectory()) { 809 outputDirectory.mkdirs(); 810 } 811 ArrayList<FileSystem.Classpath> paths = new ArrayList<>(Main.DEFAULT_SIZE_CLASSPATH); 812 try { 813 (new Main(new PrintWriter(System.out), new PrintWriter(System.err), true/*systemExit*/, null/*options*/, null/*progress*/)). 814 processPathEntries(Main.DEFAULT_SIZE_CLASSPATH, paths, classpathInput, null /* customEncoding */, true /* isSourceOnly */, false /* rejectDestinationPathOnJars*/); 815 } catch (IllegalArgumentException e) { 816 // e.printStackTrace(); 817 if (expectedError == null) { 818 fail("unexpected invalid input exception: " + e.getMessage()); 819 } else if (! expectedError.equals(e.getMessage())) { 820 System.out.println("\"" + e.getMessage() + "\""); 821 assertEquals(expectedError, e.getMessage()); 822 } 823 return; 824 } 825 if (expectedError == null) { 826 int l = paths.size(); 827 assertEquals("unexpected classpaths entries number: ", 828 expectedClasspathEntries == null ? 0 : expectedClasspathEntries.length / 3, l); 829 for (int i = 0, j = 0; i < l ; i++) { 830 ClasspathLocation result = (ClasspathLocation) paths.get(i); 831 String expected = expectedClasspathEntries[j++]; 832 String actual = result.toString(); 833 if (! actual.equals("ClasspathDirectory " + expected + File.separator) && 834 ! actual.equals("Classpath for jar file " + expected)) { 835 assertEquals("dir/jar " + expected, actual); 836 } 837 expected = expectedClasspathEntries[j++]; 838 if (result.accessRuleSet == null) { 839 assertNull("actual access rule is null instead of <" + expected +">", expected); 840 } else if (! result.accessRuleSet.toString(false). 841 startsWith("AccessRuleSet " + expected)) { 842 System.out.println("\"" + result.accessRuleSet.toString(false) + "\""); 843 fail("inappropriate rules (expected " + expected + 844 ", got " + result.accessRuleSet.toString(false)); 845 } 846 expected = expectedClasspathEntries[j++]; 847 if (expected == null) { 848 assertNull(result.destinationPath); 849 } else if (expected == Main.NONE && 850 result.destinationPath != Main.NONE) { 851 fail("expected 'none' output directory"); 852 } else if (! expected.equals(result.destinationPath)) { 853 System.out.println("\"" + result.destinationPath + "\""); 854 assertEquals(expected, result.destinationPath); 855 } 856 } 857 } else { 858 fail("missing error: " + expectedError); 859 } 860 } 861 862 /** 863 * Check that no line of message extends beyond width columns. Tabs count for 864 * 4 characters. 865 * @param message the message to check 866 * @param width the maximum number of columns for the message 867 */ checkWidth(String message, int width)868 protected void checkWidth(String message, int width) { 869 BufferedReader reader = new BufferedReader( 870 new StringReader(message.replaceAll("\t", " "))); 871 String line; 872 try { 873 while ((line = reader.readLine()) != null) { 874 assertTrue("line exceeds " + width + "characters: " + line, 875 line.length() <= width); 876 } 877 } catch (IOException e) { 878 // should never happen on a StringReader 879 } 880 } 881 equals(String a, String b)882 private static boolean equals(String a, String b) { 883 StringBuffer aBuffer = new StringBuffer(a), bBuffer = new StringBuffer(b); 884 int length = aBuffer.length(), bLength; 885 boolean result = true; 886 if (length != (bLength = bBuffer.length())) { 887 System.err.println("a and b lengths differ"); 888 if (length > bLength) { 889 length = bLength; 890 } 891 result = false; 892 } 893 for (int i = 0; i < length; i++) 894 if (aBuffer.charAt(i) != bBuffer.charAt(i)) { 895 int beforeStart = i - 5, beforeEnd = i - 1, afterStart = i + 1, afterEnd = i + 5; 896 if (beforeStart < 0) { 897 beforeStart = 0; 898 if (beforeEnd < 0) 899 beforeEnd = 0; 900 } 901 if (afterEnd >= length) { 902 afterEnd = length - 1; 903 if (afterStart >= length) 904 afterStart = length - 1; 905 } 906 System.err.println("a and b differ at rank: " + i 907 + "\na: ..." + aBuffer.substring(beforeStart, beforeEnd) 908 + "<" + aBuffer.charAt(i) + ">" 909 + aBuffer.substring(afterStart, afterEnd) + "..." 910 + "\nb: ..." + bBuffer.substring(beforeStart, beforeEnd) 911 + "<" + bBuffer.charAt(i) + ">" 912 + bBuffer.substring(afterStart, afterEnd) + "..."); 913 return false; 914 } 915 return result; // may be false if one of the strings equals the beginning 916 // of the other one, which is longer anyway 917 } 918 919 /** 920 * Return true if and only if the two strings passed as parameters compare 921 * equal, modulo the transformation of the second string by a normalizer 922 * passed in parameter. This is meant to erase the variations of subparts of 923 * the compared strings in function of the test machine, the user account, 924 * etc. 925 * 926 * @param keep 927 * the first string to compare, gets compared as it is 928 * @param normalize 929 * the second string to compare, passed through the normalizer 930 * before comparison 931 * @param normalizer 932 * the transformation applied to normalize 933 * @return true if keep and normalize compare equal after normalize has been 934 * normalized 935 */ semiNormalizedComparison(String keep, String normalize, Normalizer normalizer)936 protected boolean semiNormalizedComparison(String keep, String normalize, Normalizer normalizer) { 937 if (keep == null) 938 return normalize == null; 939 if (normalize == null) 940 return false; 941 // return keep.equals(normalizer.normalized(normalize)); 942 return equals(keep, normalizer.normalized(normalize)); 943 } 944 945 } 946