/******************************************************************************* * Copyright (c) 2000, 2015 IBM Corporation 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: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.jdt.core.tests.formatter; import java.io.BufferedOutputStream; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; import java.io.FileFilter; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.FileWriter; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintStream; import java.net.URL; import java.text.NumberFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.StringTokenizer; import junit.framework.AssertionFailedError; import junit.framework.ComparisonFailure; import junit.framework.Test; import junit.framework.TestSuite; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.FileLocator; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.Platform; import org.eclipse.jdt.core.compiler.CategorizedProblem; import org.eclipse.jdt.core.formatter.CodeFormatter; import org.eclipse.jdt.core.formatter.DefaultCodeFormatterConstants; import org.eclipse.jdt.core.tests.model.ModelTestsUtil; import org.eclipse.jdt.core.tests.util.Util; import org.eclipse.jdt.internal.compiler.impl.CompilerOptions; import org.eclipse.jdt.internal.compiler.problem.DefaultProblem; import org.eclipse.jdt.internal.core.util.CodeSnippetParsingUtil; import org.eclipse.jdt.internal.formatter.DefaultCodeFormatter; import org.eclipse.jdt.internal.formatter.DefaultCodeFormatterOptions; import org.eclipse.text.edits.TextEdit; /** * Comment formatter test suite for massive tests at a given location. * <p> * This test suite has only one generic test. When running this test suite, one test * is created per compilation unit found while traversing the directory specified * using the <code>inputDir</code> system property<br>(e.g. * <code>-DinputDir=D:\eclipse\workspaces\formatter\inputs\full-src-30</code>). * </p><p> * Each test formats twice the compilation unit and compare the result to a * previous formatting already stored at the same location from a root directory * specified using the <code>outputDir</code> system property. * </p><p> * For example, if <code>outputDir</code> is set to the following value:<br> * <code>-DoutputDir=D:\eclipse\workspaces\formatter\outputs</code><br> * then a compilation unit found in <code>...\inputs\full-src-30\test\A.java</code> * will be compared with the previously stored output in: * <code>...\outputs\full-src-30\test\A.java</code> * </p><p> * To store the outputs on a specific input directory using a specific version, * then load a JDT/Core version in the workspace and run this test suite using * the following VM arguments: * <pre> * -DinputDir=D:\eclipse\workspaces\formatter\inputs\full-src-30 * -DoutputDir=D:\eclipse\workspaces\formatter\outputs,clean * </pre> * Note the <code>clean</code> arguments added at the end of the outputDir * system property to signify that the formatter outputs must be cleaned and * stored. * </p><p> * The <code>logDir</code> system property can be set to tell the suite to * write the console output in a file located in the specified directory. That makes * the comparison between version and patches easier to do using the eclipse * file comparison... * </p><p><br> * <b>***************************************<br> * * Process to run massive tests against a patch *<br> * ***************************************</b> * <p> * Here is the full description of the process to run massive tests against a patch * using this test suite and all the compilation units of the JDT/Core performance * <b>full-source-R3_0.zip</b> file... * </p> * <h3>Set-up input directory</h3> * <p> * The test suite needs to know where are the sources which will be used * to massively test the formatter. To make it easy to set-up, only a root directory * is necessary. From there, all compilation units found while traversing the tree of * this directory will be used for the massive tests. * </p><p> * In our example, we will extract the content of the <b>full-source-R3_0.zip</b> * file located in the <b>org.eclipse.jdt.core.tests.performance</b> plugin * somewhere on our local disk... let's say in the * <b>D:\tmp\formatter\inputs\full-src-30</b> directory. * </p> * <h3>Create the output reference</h3> * <p> * The reference from which the patch output will be compared to while running * the massive test needs also to be created. To do this, a launch config * for the <code>FormatterMassiveRegressionTests</code> test suite is necessary. * </p><p> * For example, create a launch config named * <b>FormatterMassiveRegressionTests (Eclipse 3.0 - clean)</b> with the * following VM arguments: * <pre> * -Xmx256M * -DinputDir=D:\tmp\formatter\inputs\full-src-30 * -DoutputDir=D:\tmp\formatter\outputs,clean * -DlogDir=D:\tmp\formatter\log * </pre> * </p><p> * Load the last version of JDT/Core plugins (e.g. <code>v_B11</code>) and * launch this config... * </p><p> * When done, the console should have the following content: * <pre> * Get all files from D:\tmp\formatter\inputs\full-src-30...done * Deleting all files from D:\tmp\formatter\outputs\v37\full-src-30...done * Version : v_B11 * Profiles : none! * Test date : 9/12/10 1:47 PM * Input dir : D:\tmp\formatter\inputs\full-src-30 * 9950 java files to format... * Output dir: D:\tmp\formatter\outputs\v37\full-src-30 * CLEANED * </pre> * Looking at the output directory, it should contain the same folders tree than * the input one... * </p> * <h3>Create the log reference</h3> * <p> * The test suite log several problems which may occur while formatting a unit: * <ul> * <li>the file may have compilation errors preventing the formatter to proceed</li> * <li>there's no output while formatting</li> * <li>the output may be different while formatting twice</li> * <li>the output may be different while formatting twice but only by leading whitespaces</li> * <li>the output may be different while formatting twice but only by whitespaces</li> * </ul> * </p><p> * Even with last version of the formatter, such problems may happen on one or * several tested compilation unit. So, it's important to know which are the existing * issues of the used formatter version (e.g. <code>v_B11</code> in our example...). * </p><p> * To do this, another launch config is necessary to run the massive tests of the * loaded JDT/Core version. * </p><p> * For example, copy the previous launch config and rename it * <b>FormatterMassiveRegressionTests (Eclipse 3.0)</b>. Change the VM * arguments as follows (<i>note that the <code>clean</code> has been removed * from the <code>outputDir</code> system property</i>): * <pre> * -Xmx256M * -DinputDir=D:\tmp\formatter\inputs\full-src-30 * -DoutputDir=D:\tmp\formatter\outputs * -DlogDir=D:\tmp\formatter\log * </pre> * </p><p> * Launch the config... * </p><p> * The log file contains the console output but also the complete list of the units * on which problems were observed. As this run was done with the JDT/Core * version it can be considered as the reference for this version... * </p><p> * Note that for our example, the observed problems for <code>v_B11</code> * version while running massive tests on a Eclipse 3.0 performance workspace * (9951 units) are: * <ul> * <li>1 file has compilation errors which prevent the formatter to proceed!</li> * <li>4 files have different output while reformatting twice!</li> * <li>10 files have different output while reformatting twice but only by leading whitespaces!</li> * <li>4 files have different output while reformatting twice but only by whitespaces!</li> * </ul> * </p> * <h3>Run the massive tests on the patch</h3> * <p> * As the setup has been done for the massive tests, it's now possible to test a * patch applied on the reference version (<code>v_B11</code>). For this, the * patch needs of course to be applied first and also the <b>buildnotes_jdt-core.html</b> * modified. * </p><p> * If the patch vXX of bug XXXXXX is about to be tested, then the line * <code>Patch vXX for bug XXXXXX</code> needs to be added at the * beginning of the first <b>What's new in this drop</b> section of the * <b>buildnotes_jdt-core.html</b> file, e.g.: * <pre> * <h2>What's new in this drop</h2> * Patch v05 for bug 303519 * <ul> * ... * </pre> * </p><p> * Launch the <b>FormatterMassiveRegressionTests (Eclipse 3.0)</b> config... * </p><p> * Like the previous run, the written log file contains the complete list of the units * on which problems were observed. Comparing this log file with the reference one * will show whether the patch implies behavior changes for the formatter or not. * </p> */ @SuppressWarnings({"rawtypes", "unchecked"}) public class FormatterMassiveRegressionTests extends FormatterRegressionTests { final File file; final IPath path; private DefaultCodeFormatterOptions preferences; // Directories // private final static File INPUT_DIR = new File(System.getProperty("inputDir")); private final File inputDir; private static File OUTPUT_DIR; // use static to minimize data consumption private static File WRITE_DIR; // Files final static String FILES_FILTER = System.getProperty("filesFilter"); final static int FILES_FILTER_KIND; static { int kind = 0; // No filter if (FILES_FILTER != null) { int length = FILES_FILTER.length(); int idxQM = FILES_FILTER.indexOf('?'); int idxS = FILES_FILTER.indexOf('*'); if (idxQM >= 0 && idxS >= 0) { kind = 4; // Pure pattern match } else if (idxQM >= 0) { while (idxQM < length && FILES_FILTER.charAt(idxQM) == '?') { idxQM++; } if (idxQM == length) { kind = 3; // Starts with + same length } else { kind = 4; // Pure pattern match } } else if (idxS >= 0) { while (idxS < length && FILES_FILTER.charAt(idxQM) == '*') { idxS++; } if (idxS == length) { kind = 2; // Starts with } else { kind = 4; // Pure pattern match } } else { kind = 1; // Equals } } FILES_FILTER_KIND = kind; } // Log private static File LOG_FILE; private static PrintStream LOG_STREAM; // Maintenance private static boolean MAINTENANCE = false; // Comparison private static boolean CLEAN = false; private static boolean CAN_COMPARE = true; private final boolean canCompare; private final int testIndex; // Cleaning private static boolean LIST = false; private final static Map MAX_FILES = new HashMap(); // Formatting behavior final static int FORMAT_REPEAT = Integer.parseInt(System.getProperty("repeat", "2")); private final static boolean NO_COMMENTS = System.getProperty("no_comments", "false").equals("true"); private final static String JOIN_LINES = System.getProperty("join_lines", null); private final static String BRACES = System.getProperty("braces", null); private final static int PRESERVED_LINES; static { String str = System.getProperty("preserved_lines", null); int value = -1; if (str != null) { try { value = Integer.parseInt(str); } catch (NumberFormatException nfe) { // skip } } PRESERVED_LINES = value; } private final int profiles; private final static int PROFILE_NEVER_JOIN_LINES = 1; private final static int PROFILE_JOIN_LINES_ONLY_COMMENTS = 2; private final static int PROFILE_JOIN_LINES_ONLY_CODE = 3; private final static int PROFILE_JOIN_LINES_MASK = 0x0003; private final static int PROFILE_NO_COMMENTS = 1 << 2; private final static int PROFILE_BRACES_NEXT_LINE = 1 << 3; private final static int PROFILE_BRACES_NEXT_LINE_ON_WRAP = 2 << 3; private final static int PROFILE_BRACES_NEXT_LINE_SHIFTED = 3 << 3; private final static int PROFILE_BRACES_MASK = 0x0018; private final static int PROFILE_PRESERVED_LINES_MASK = 0x00E0; // Time measuring static class TimeMeasuring { long[] formatting = new long[FORMAT_REPEAT]; int [] occurences = new int[FORMAT_REPEAT]; int [] null_output = new int[FORMAT_REPEAT]; } private static TimeMeasuring TIME_MEASURES; private static final int ONE_MINUTE = 60000; private static final long ONE_HOUR = 3600000L; // Failures management int failureIndex; final static int UNEXPECTED_FAILURE = 0; final static int NO_OUTPUT_FAILURE = 1; final static int COMPILATION_ERRORS_FAILURE = 2; final static int FILE_NOT_FOUND_FAILURE = 3; final static int COMPARISON_FAILURE = 4; final static int REFORMATTING_FAILURE = 5; final static int REFORMATTING_EXPECTED_FAILURE = 6; final static int REFORMATTING_LEADING_FAILURE = 7; final static int REFORMATTING_WHITESPACES_FAILURE = 8; static class FormattingFailure { String msg; int kind; List failures = new ArrayList(); public FormattingFailure(int kind) { this.kind = kind; } public FormattingFailure(int kind, String msg) { this(kind); this.msg = msg; } int size() { return this.failures.size(); } public String toString() { switch (this.kind) { case UNEXPECTED_FAILURE: return "unexpected failure while formatting"; case NO_OUTPUT_FAILURE: return "no output while formatting"; case COMPILATION_ERRORS_FAILURE: return "compilation errors which prevent the formatter to proceed"; case FILE_NOT_FOUND_FAILURE: return "no formatted output to compare with"; case COMPARISON_FAILURE: return "different output while comparing with previous version"; default: return "different output while "+this.msg; } } } static FormattingFailure[] FAILURES; private static final int MAX_FAILURES = Integer.parseInt(System.getProperty("maxFailures", "100")); // Max failures using string comparison private static boolean ASSERT_EQUALS_STRINGS = MAX_FAILURES > 0; private static String ECLIPSE_VERSION; private static String ECLIPSE_MILESTONE; private static String JDT_CORE_VERSION; private static String PATCH_BUG, PATCH_VERSION; private static String TEMP_OUTPUT; private static boolean JDT_CORE_HEAD; /* private final static IPath[] EXPECTED_FAILURES = INPUT_DIR.getPath().indexOf("v34") < 0 ? new IPath[] { new Path("org/eclipse/jdt/internal/compiler/ast/QualifiedNameReference.java"), new Path("org/eclipse/jdt/internal/eval/CodeSnippetSingleNameReference.java"), new Path("org/eclipse/jdt/internal/core/DeltaProcessor.java"), new Path("org/eclipse/jdt/internal/core/JavaProject.java"), new Path("org/eclipse/jdt/internal/core/search/indexing/IndexManager.java"), new Path("org/eclipse/team/internal/ccvs/ui/AnnotateView.java"), new Path("org/eclipse/team/internal/ccvs/ui/HistoryView.java"), new Path("org/eclipse/team/internal/ccvs/ui/wizards/UpdateWizard.java"), } : new IPath[] { // Eclipse new Path("org/eclipse/equinox/internal/p2/director/NewDependencyExpander.java"), new Path("org/eclipse/jdt/core/JavaCore.java"), new Path("org/eclipse/jdt/internal/codeassist/CompletionEngine.java"), new Path("org/eclipse/jdt/internal/codeassist/SelectionEngine.java"), new Path("org/eclipse/jdt/internal/compiler/ast/Expression.java"), new Path("org/eclipse/jdt/internal/compiler/ast/QualifiedNameReference.java"), new Path("org/eclipse/jdt/internal/compiler/ast/SingleNameReference.java"), new Path("org/eclipse/jdt/internal/eval/CodeSnippetSingleNameReference.java"), new Path("org/eclipse/jdt/internal/compiler/lookup/WildcardBinding.java"), new Path("org/eclipse/jdt/internal/compiler/batch/Main.java"), new Path("org/eclipse/jdt/internal/compiler/lookup/ParameterizedMethodBinding.java"), new Path("org/eclipse/jdt/internal/core/CompilationUnit.java"), new Path("org/eclipse/jdt/internal/core/ExternalJavaProject.java"), new Path("org/eclipse/jdt/internal/core/hierarchy/HierarchyResolver.java"), new Path("org/eclipse/jdt/internal/core/hierarchy/TypeHierarchy.java"), new Path("org/eclipse/jdt/internal/core/search/indexing/IndexAllProject.java"), new Path("org/eclipse/jdt/internal/core/search/JavaSearchScope.java"), new Path("org/eclipse/jdt/internal/eval/EvaluationContext.java"), new Path("org/eclipse/jdt/internal/ui/text/javadoc/JavadocContentAccess2.java"), new Path("org/eclipse/jdt/internal/apt/pluggable/core/filer/IdeJavaSourceOutputStream.java"), new Path("org/eclipse/team/internal/ccvs/ui/mappings/WorkspaceSubscriberContext.java"), // Ganymede new Path("com/ibm/icu/text/Collator.java"), new Path("org/apache/lucene/analysis/ISOLatin1AccentFilter.java"), }; */ public static Test suite() { return suite(new File(System.getProperty("inputDir")), buildProfileString(), new HashMap()); } protected static Test suite(File inputDir, String profile, Map directories) { String name = "FormatterMassiveRegressionTests on "+inputDir.getName(); if (profile != null && profile.length() > 0) { name += " " + profile; } TestSuite suite = new Suite(name); try { // Init version initVersion(); // Init profiles int profiles = initProfiles(profile); // Init directories initDirectories(inputDir, profiles, true); // Get files from input dir FileFilter filter = new FileFilter() { public boolean accept(File pathname) { String path = pathname.getPath(); if (pathname.isDirectory()) { String dirName = path.substring(path.lastIndexOf(File.separatorChar)+1); return !dirName.equals("bin"); } if (path.endsWith(".java")) { if (FILES_FILTER_KIND > 0) { String fileName = path.substring(path.lastIndexOf(File.separatorChar)+1); switch (FILES_FILTER_KIND) { case 1: // Equals return fileName.equals(FILES_FILTER); case 2: // Starts with return fileName.startsWith(FILES_FILTER); case 3: // Starts with + same length return fileName.startsWith(FILES_FILTER) && fileName.length() == FILES_FILTER.length(); case 4: // Pattern return fileName.matches(FILES_FILTER); } } else { return true; } } return false; } }; File[] allFiles = (File[]) directories.get(inputDir); File listFile = new File(inputDir.getParentFile(), inputDir.getName()+".lst"); BufferedWriter listFileWriter = null; if (allFiles == null) { System.out.print("Get all files from "); if (LIST || !listFile.exists()) { // Get the files list System.out.print(inputDir+"..."); allFiles = ModelTestsUtil.getAllFiles(inputDir, filter); // Delete the files list if (listFile.exists()) { listFile.delete(); } // Initialize the files list writer listFileWriter = new BufferedWriter(new FileWriter(listFile)); listFileWriter.write(Integer.toString(allFiles.length)); listFileWriter.newLine(); } else { System.out.print("stored list in "+listFile.getPath()+"..."); BufferedReader listFileReader = new BufferedReader(new InputStreamReader(new FileInputStream(listFile.getAbsolutePath()))); try { // First line is the number of files String line = listFileReader.readLine(); int maxFiles = Integer.parseInt(line); // Following lines are the files path allFiles = new File[maxFiles]; for (int i=0; i<maxFiles; i++) { allFiles[i] = new File(inputDir, listFileReader.readLine()); if (!allFiles[i].exists()) { throw new IOException("Cannot find file "+allFiles[i]); } } } catch (NumberFormatException nfe) { nfe.printStackTrace(); return null; } catch (IOException ioe) { ioe.printStackTrace(); return null; } finally { listFileReader.close(); } } directories.put(inputDir, allFiles); System.out.println("done"); } int[] maxFiles = new int[2]; maxFiles[0] = allFiles.length; maxFiles[1] = (int) (Math.log(maxFiles[0])/Math.log(10)); MAX_FILES.put(inputDir, maxFiles); // Add tests to clean the output directory and rebuild the references // if (CLEAN) { // suite.addTest(new FormatterMassiveRegressionTests(profiles)); // } // Add one test per found file try { final int inputDirPathLength = inputDir.getPath().length()+1; for (int i=0; i<maxFiles[0]; i++) { if (CLEAN) { suite.addTest(new FormatterMassiveRegressionTests(inputDir, allFiles[i], i, profiles, false/*do not compare while cleaning*/)); } else { suite.addTest(new FormatterMassiveRegressionTests(inputDir, allFiles[i], i, profiles, CAN_COMPARE)); } if (listFileWriter != null) { listFileWriter.write(allFiles[i].getPath().substring(inputDirPathLength)); listFileWriter.newLine(); } } } finally { if (listFileWriter != null) { listFileWriter.close(); } } } catch (Exception e) { e.printStackTrace(); } return suite; } private static String buildProfileString() { boolean hasProfile = NO_COMMENTS || PRESERVED_LINES != -1; if (JOIN_LINES != null) { if (JOIN_LINES.equals("never") || JOIN_LINES.equals("only_comments") || JOIN_LINES.equals("only_code")) { hasProfile = true; } } if (BRACES != null) { if (BRACES.equals(DefaultCodeFormatterConstants.NEXT_LINE) || BRACES.equals(DefaultCodeFormatterConstants.NEXT_LINE_ON_WRAP) || BRACES.equals(DefaultCodeFormatterConstants.NEXT_LINE_SHIFTED)) { hasProfile = true; } } String builtProfile = null; if (hasProfile) { StringBuffer buffer = new StringBuffer(); String separator = ""; if (JOIN_LINES != null) { buffer.append("join_lines="+JOIN_LINES); separator = ","; } if (NO_COMMENTS) { buffer.append(separator+"no_comments=true"); separator = ","; } if (BRACES != null) { buffer.append(separator+"braces="+BRACES); separator = ","; } if (PRESERVED_LINES != -1) { buffer.append(separator+"preserved_lines="+PRESERVED_LINES); separator = ","; } builtProfile = buffer.toString(); } // Return built profile string return builtProfile; } private static int initProfiles(String profile) { if (profile == null || profile.length() == 0) return 0; StringTokenizer tokenizer = new StringTokenizer(profile, ","); int profiles = 0; while (tokenizer.hasMoreTokens()) { String token = tokenizer.nextToken(); int idx = token.indexOf('='); if (idx <= 0) { System.err.println("'"+profile+"' is not a valid profile!!!"); return 0; } String profileName = token.substring(0, idx); if (profileName.equals("join_lines")) { String joinLines = token.substring(idx+1); if (joinLines.equals("never")) { profiles += PROFILE_NEVER_JOIN_LINES; } else if (joinLines.equals("only_comments")) { profiles += PROFILE_JOIN_LINES_ONLY_COMMENTS; } else if (joinLines.equals("only_code")) { profiles += PROFILE_JOIN_LINES_ONLY_CODE; } } else if (profileName.equals("no_comments")) { String noComments = token.substring(idx+1); if (noComments.equals(DefaultCodeFormatterConstants.TRUE)) { profiles |= PROFILE_NO_COMMENTS; } } else if (profileName.equals("braces")) { String braces = token.substring(idx+1); if (braces.equals(DefaultCodeFormatterConstants.NEXT_LINE)) { profiles += PROFILE_BRACES_NEXT_LINE; } else if (braces.equals(DefaultCodeFormatterConstants.NEXT_LINE_ON_WRAP)) { profiles += PROFILE_BRACES_NEXT_LINE_ON_WRAP; } else if (braces.equals(DefaultCodeFormatterConstants.NEXT_LINE_SHIFTED)) { profiles += PROFILE_BRACES_NEXT_LINE_SHIFTED; } } else if (profileName.equals("preserved_lines")) { try { String lines = token.substring(idx+1); int value = Integer.parseInt(lines); if (value >= 0 && value < 8) { profiles += value << 5; } } catch (NumberFormatException nfe) { // skip } } } return profiles; } private static void initDirectories(File inputDir, int profiles, boolean verify) { // Verify input directory if (!inputDir.exists() && !inputDir.isDirectory()) { System.err.println(inputDir+" does not exist or is not a directory!"); System.exit(1); } // Get output dir and clean it if specified String dir = System.getProperty("outputDir"); //$NON-NLS-1$ if (dir != null) { StringTokenizer tokenizer = new StringTokenizer(dir, ","); String outputDir = tokenizer.nextToken(); while (tokenizer.hasMoreTokens()) { String token = tokenizer.nextToken(); if (token.equals("clean")) { CLEAN = true; } else if (token.equals("maintenance")) { MAINTENANCE = true; } else if (token.equals("list")) { LIST = true; } else { TEMP_OUTPUT = token; } } setOutputDir(inputDir, outputDir, profiles); if (CLEAN) { if ((PATCH_BUG != null || JDT_CORE_HEAD) && TEMP_OUTPUT == null) { System.err.println("Reference can only be updated using a version (i.e. with a closed buildnotes_jdt-core.html)!"); System.exit(1); } return; } else if (!OUTPUT_DIR.exists()) { System.err.println(" WARNING: The output directory "+OUTPUT_DIR+" does not exist..."); System.err.println(" => NO comparison could be done!"); CAN_COMPARE = false; } try { Thread.sleep(1000); } catch (InterruptedException e) { // skip } } // Get log dir try { setLogDir(inputDir, profiles, verify); } catch (CoreException e) { e.printStackTrace(); } // Get write dir String wdir = System.getProperty("writeDir"); //$NON-NLS-1$ if (wdir != null) { WRITE_DIR = new File(wdir); if (WRITE_DIR.exists()) { Util.delete(WRITE_DIR); } WRITE_DIR.mkdirs(); } } private static void setLogDir(File inputDir, int profiles, boolean verify) throws CoreException { // Compute log dir File rootLogDir = new File(System.getProperty("logDir")); if (!rootLogDir.exists()) { if (!rootLogDir.mkdirs()) { System.err.println("Cannot create specified log directory: "+rootLogDir+"!!!"); return; } } // Compute log sub-directories depending on version rootLogDir = new File(rootLogDir, ECLIPSE_VERSION); String subRootDir; if (PATCH_BUG != null) { rootLogDir = new File(rootLogDir, "tests"); rootLogDir = new File(rootLogDir, PATCH_BUG); subRootDir = PATCH_VERSION; } else if (JDT_CORE_HEAD) { subRootDir = "HEAD"; } else { rootLogDir = new File(rootLogDir, ECLIPSE_MILESTONE); subRootDir = JDT_CORE_VERSION; } // Compute log sub-directories depending on profiles List subDirs = new ArrayList(); if (profiles > 0) { subDirs.add("profiles"); setProfilesDir(profiles, subDirs); } if (FILES_FILTER_KIND > 0) { subDirs.add("filter"); subDirs.add(FILES_FILTER.replace('?', '_').replace('*', '%')); } // Create log stream File logDir = createDir(new File (rootLogDir, subRootDir), subDirs); String filePrefix = inputDir.getName().replaceAll("\\.", ""); String logFileName = filePrefix+".txt"; LOG_FILE = new File(logDir, logFileName); if (verify && LOG_FILE.exists()) { File saveDir = new File(new File(rootLogDir, "save"), subRootDir); saveDir.mkdirs(); int i=0; while (true) { String dirN = Integer.toString(i); if (i<10) dirN = "0" + dirN; saveDir = new File(saveDir, dirN); logDir = createDir(saveDir, subDirs); File renamedFile = new File(logDir, logFileName); if (LOG_FILE.renameTo(renamedFile)) break; i++; } } // LOG_RESOURCE = folder.getFile(logFileName); try { LOG_STREAM = new PrintStream(new BufferedOutputStream(new FileOutputStream(LOG_FILE))); LOG_STREAM.flush(); } catch (FileNotFoundException fnfe) { System.err.println("Can't create log file"+LOG_FILE); //$NON-NLS-1$ } // if (LOG_RESOURCE.exists()) { // Util.delete(LOG_RESOURCE); // } // LOG_BUFFER = new StringBuffer(); } private static File createDir(File rootDir, List subDirs) { File dir = rootDir; for (int i=0, s=subDirs.size(); i<s; i++) { dir = new File (dir, (String) subDirs.get(i)); } dir.mkdirs(); return dir; } private static File setProfilesDir(int profiles, File dir) { List subDirs = new ArrayList(); setProfilesDir(profiles, subDirs); return createDir(dir, subDirs); } private static void setProfilesDir(int profiles, List subDirs) { String joinLines = null; switch (profiles & PROFILE_JOIN_LINES_MASK) { case PROFILE_NEVER_JOIN_LINES: joinLines = "never"; break; case PROFILE_JOIN_LINES_ONLY_COMMENTS: joinLines = "only_comments"; break; case PROFILE_JOIN_LINES_ONLY_CODE: joinLines = "only_code"; break; } if (joinLines != null) { subDirs.add("join_lines"); subDirs.add(joinLines); } if ((profiles & PROFILE_NO_COMMENTS) != 0) { subDirs.add("no_comments"); } String braces = null; switch (profiles & PROFILE_BRACES_MASK) { case PROFILE_BRACES_NEXT_LINE: braces = DefaultCodeFormatterConstants.NEXT_LINE; break; case PROFILE_BRACES_NEXT_LINE_ON_WRAP: braces = DefaultCodeFormatterConstants.NEXT_LINE_ON_WRAP; break; case PROFILE_BRACES_NEXT_LINE_SHIFTED: braces = DefaultCodeFormatterConstants.NEXT_LINE_SHIFTED; break; } if (braces != null) { subDirs.add("braces"); subDirs.add(braces); } if ((profiles & PROFILE_PRESERVED_LINES_MASK) != 0) { int lines = (profiles & PROFILE_PRESERVED_LINES_MASK) >> 5; subDirs.add("preserved_lines"); subDirs.add(Integer.toString(lines)); } } private static void appendProfiles(int profiles, StringBuffer buffer) { String joinLines = null; boolean first = true; switch (profiles & PROFILE_JOIN_LINES_MASK) { case PROFILE_NEVER_JOIN_LINES: joinLines = "never"; break; case PROFILE_JOIN_LINES_ONLY_COMMENTS: joinLines = "only_comments"; break; case PROFILE_JOIN_LINES_ONLY_CODE: joinLines = "only_code"; break; } if (joinLines != null) { buffer.append("join_lines="); buffer.append(joinLines); first = false; } if ((profiles & PROFILE_NO_COMMENTS) != 0) { if (!first) buffer.append(','); buffer.append("no_comments"); first = false; } String braces = null; switch (profiles & PROFILE_BRACES_MASK) { case PROFILE_BRACES_NEXT_LINE: braces = DefaultCodeFormatterConstants.NEXT_LINE; break; case PROFILE_BRACES_NEXT_LINE_ON_WRAP: braces = DefaultCodeFormatterConstants.NEXT_LINE_ON_WRAP; break; case PROFILE_BRACES_NEXT_LINE_SHIFTED: braces = DefaultCodeFormatterConstants.NEXT_LINE_SHIFTED; break; } if (braces != null) { if (!first) buffer.append(','); buffer.append("braces="); buffer.append(braces); first = false; } if ((profiles & PROFILE_PRESERVED_LINES_MASK) != 0) { int lines = (profiles & PROFILE_PRESERVED_LINES_MASK) >> 5; if (!first) buffer.append(','); buffer.append("preserved_lines="); buffer.append(lines); first = false; } if (first) { buffer.append("none!"); } } private static void setOutputDir(File inputDir, String dir, int profiles) { // Find the root of the output directory OUTPUT_DIR = new File(dir); if (OUTPUT_DIR.getName().equals(inputDir.getName())) { OUTPUT_DIR = OUTPUT_DIR.getParentFile(); } if (OUTPUT_DIR.getName().equals(ECLIPSE_VERSION)) { OUTPUT_DIR = OUTPUT_DIR.getParentFile(); } // Add the temporary output if any if (TEMP_OUTPUT != null) { StringTokenizer tokenizer = new StringTokenizer(TEMP_OUTPUT, "/"); while (tokenizer.hasMoreTokens()) { OUTPUT_DIR = new File(OUTPUT_DIR, tokenizer.nextToken()); } } // Compute output sub-directories depending on profiles if (profiles > 0) { OUTPUT_DIR = new File(OUTPUT_DIR, "profiles"); OUTPUT_DIR = setProfilesDir(profiles, OUTPUT_DIR); } // Compute the final output dir File parent = new File(OUTPUT_DIR, ECLIPSE_VERSION); if (MAINTENANCE) { try { int version = Integer.parseInt(ECLIPSE_VERSION.substring(1)); File maintenance = new File(OUTPUT_DIR, "v"+(version-1)); if (maintenance.exists()) { parent = maintenance; } } catch (NumberFormatException nfe) { // skip } } OUTPUT_DIR = new File(parent, inputDir.getName()); } private static void initFailures() { FAILURES = new FormattingFailure[REFORMATTING_WHITESPACES_FAILURE+1]; for (int i=UNEXPECTED_FAILURE; i<=COMPARISON_FAILURE; i++) { FAILURES[i] = new FormattingFailure(i); } FAILURES[REFORMATTING_FAILURE] = new FormattingFailure(REFORMATTING_FAILURE, "reformatting twice"); FAILURES[REFORMATTING_LEADING_FAILURE] = new FormattingFailure(REFORMATTING_LEADING_FAILURE, "reformatting twice but only by leading whitespaces"); FAILURES[REFORMATTING_WHITESPACES_FAILURE] = new FormattingFailure(REFORMATTING_WHITESPACES_FAILURE, "reformatting twice but only by whitespaces"); FAILURES[REFORMATTING_EXPECTED_FAILURE] = new FormattingFailure(REFORMATTING_EXPECTED_FAILURE, "reformatting twice but was expected"); } /* * Read JDT/Core build notes file to see what version is currently running. */ private static void initVersion() { if (JDT_CORE_VERSION == null) { BufferedReader buildnotesReader; try { URL platformURL = Platform.getBundle("org.eclipse.jdt.core").getEntry("/"); String path = new File(FileLocator.toFileURL(platformURL).getFile(), "buildnotes_jdt-core.html").getAbsolutePath(); buildnotesReader = new BufferedReader(new InputStreamReader(new FileInputStream(path))); } catch (IOException ioe) { ioe.printStackTrace(); return; } String line; JDT_CORE_HEAD = true; try { while ((line = buildnotesReader.readLine()) != null) { if (line.startsWith("<a name=\"")) { boolean first = JDT_CORE_VERSION == null; JDT_CORE_VERSION = line.substring(line.indexOf('"')+1, line.lastIndexOf('"')); if (!first) break; } else if (line.startsWith("Eclipse SDK ")) { StringTokenizer tokenizer = new StringTokenizer(line); tokenizer.nextToken(); // 'Eclipse' tokenizer.nextToken(); // 'SDK' String milestone = tokenizer.nextToken(); ECLIPSE_VERSION = "v"+milestone.charAt(0)+milestone.charAt(2); ECLIPSE_MILESTONE = milestone.substring(3); tokenizer.nextToken(); // '-' JDT_CORE_HEAD = tokenizer.nextToken().equals("%date%"); } else if (line.startsWith("<h2>What's new")) { line = buildnotesReader.readLine(); if (line.startsWith("Patch")) { StringTokenizer tokenizer = new StringTokenizer(line); tokenizer.nextToken(); // 'Patch' PATCH_VERSION = tokenizer.nextToken(); while (tokenizer.hasMoreTokens()) { PATCH_BUG = tokenizer.nextToken(); } try { Integer.parseInt(PATCH_BUG); } catch (NumberFormatException nfe) { // try to split StringTokenizer bugTokenizer = new StringTokenizer(PATCH_BUG, "+"); try { while (bugTokenizer.hasMoreTokens()) { Integer.parseInt(bugTokenizer.nextToken()); } } catch (NumberFormatException nfe2) { System.err.println("Invalid patch bug number noticed in JDT/Core buildnotes: "+PATCH_BUG); } } } if (!JDT_CORE_HEAD) break; } } } catch (Exception e) { try { buildnotesReader.close(); } catch (IOException ioe) { ioe.printStackTrace(); } } } } /* * Constructor used to clean the output directory. * public FormatterMassiveRegressionTests(int profiles) { super("testDeleteOutputDir"); this.canCompare = false; this.file = null; this.inputDir = OUTPUT_DIR; this.testIndex = -1; this.profiles = profiles; this.path = new Path(OUTPUT_DIR.getPath()); } /* * Constructor used to dump references in the output directory. * public FormatterMassiveRegressionTests(File[] files) { super("testMakeReferences"); assertNotNull("This test needs some files to proceed!", files); this.canCompare = false; this.file = null; this.inputFiles = files; this.testIndex = -1; this.path = new Path(OUTPUT_DIR.getPath()); } /* * Contructor used to compare outputs. */ public FormatterMassiveRegressionTests(File inputDir, File file, int index, int profiles, boolean compare) { super(CLEAN ? "testReference" : "testCompare"); this.canCompare = compare; this.file = file; this.inputDir = inputDir; this.testIndex = index; this.profiles = profiles; this.path = new Path(file.getPath().substring(inputDir.getPath().length()+1)); } /* (non-Javadoc) * @see junit.framework.TestCase#getName() */ public String getName() { StringBuffer name = new StringBuffer(super.getName()); if (this.testIndex >= 0) { int n = this.testIndex == 0 ? 0 : (int) (Math.log(this.testIndex)/Math.log(10)); int max = ((int[])MAX_FILES.get(this.inputDir))[1]; for (int i=n; i<max; i++) { name.append('0'); } name.append(this.testIndex); } if (this.profiles > 0) { name.append('_'); name.append(this.profiles); } name.append(" - "); name.append(this.path); return name.toString(); } /* (non-Javadoc) * @see org.eclipse.jdt.core.tests.formatter.FormatterRegressionTests#setUpSuite() */ public void setUp() throws Exception { super.setUp(); // Setup preferences this.preferences = DefaultCodeFormatterOptions.getEclipseDefaultSettings(); // Setup no comments profile if ((this.profiles & PROFILE_NO_COMMENTS) != 0) { this.preferences.comment_format_javadoc_comment = false; this.preferences.comment_format_block_comment = false; this.preferences.comment_format_line_comment = false; } // Setup join lines profile String joinLines = null; switch (this.profiles & PROFILE_JOIN_LINES_MASK) { case PROFILE_NEVER_JOIN_LINES: joinLines = "never"; break; case PROFILE_JOIN_LINES_ONLY_COMMENTS: joinLines = "only_comments"; break; case PROFILE_JOIN_LINES_ONLY_CODE: joinLines = "only_code"; break; } if (joinLines != null) { if (!joinLines.equals("only_comments")) { this.preferences.join_lines_in_comments = false; } if (!joinLines.equals("only_code")) { this.preferences.join_wrapped_lines = false; } } // Setup braces profile String braces = null; switch (this.profiles & PROFILE_BRACES_MASK) { case PROFILE_BRACES_NEXT_LINE: braces = DefaultCodeFormatterConstants.NEXT_LINE; break; case PROFILE_BRACES_NEXT_LINE_ON_WRAP: braces = DefaultCodeFormatterConstants.NEXT_LINE_ON_WRAP; break; case PROFILE_BRACES_NEXT_LINE_SHIFTED: braces = DefaultCodeFormatterConstants.NEXT_LINE_SHIFTED; break; } if (braces != null) { this.preferences.brace_position_for_annotation_type_declaration = braces; this.preferences.brace_position_for_anonymous_type_declaration = braces; this.preferences.brace_position_for_array_initializer = braces; this.preferences.brace_position_for_block = braces; this.preferences.brace_position_for_block_in_case = braces; this.preferences.brace_position_for_constructor_declaration = braces; this.preferences.brace_position_for_enum_constant = braces; this.preferences.brace_position_for_enum_declaration = braces; this.preferences.brace_position_for_method_declaration = braces; this.preferences.brace_position_for_switch = braces; this.preferences.brace_position_for_type_declaration = braces; } // Setup preserved lines profile if ((this.profiles & PROFILE_PRESERVED_LINES_MASK) != 0) { int lines = (this.profiles & PROFILE_PRESERVED_LINES_MASK) >> 5; this.preferences.number_of_empty_lines_to_preserve = lines; } } /* (non-Javadoc) * @see org.eclipse.jdt.core.tests.formatter.FormatterRegressionTests#setUpSuite() */ public void setUpSuite() throws Exception { // Init directories initDirectories(this.inputDir, this.profiles, false); // Delete output dir before compute reference if (CLEAN) { System.out.print("Deleting all files from "+OUTPUT_DIR+"..."); Util.delete(OUTPUT_DIR); System.out.println("done"); } // Init failure else if (this.canCompare) { initFailures(); } // Dump the version if (CLEAN) { File versionFile = new Path(OUTPUT_DIR.getPath()).append("version.txt").toFile(); OUTPUT_DIR.mkdirs(); String version = JDT_CORE_VERSION; if (TEMP_OUTPUT != null) { version += " + " + TEMP_OUTPUT; if (PATCH_BUG != null) { version += " " + PATCH_VERSION + " of " + PATCH_BUG; } } Util.writeToFile(version, versionFile.getAbsolutePath()); } // Init time measuring TIME_MEASURES = new TimeMeasuring(); // Print print(); } private void print() { StringBuffer buffer = new StringBuffer(); // Log version info buffer.append("Version : "); if (PATCH_BUG != null) { buffer.append("'Patch "); buffer.append(PATCH_VERSION); buffer.append(" for bug "); buffer.append(PATCH_BUG); buffer.append("' applied on "); } if (JDT_CORE_HEAD) { buffer.append("HEAD on top of "); } buffer.append(JDT_CORE_VERSION); buffer.append(LINE_SEPARATOR); // Profiles buffer.append("Profiles : "); appendProfiles(this.profiles, buffer); buffer.append(LINE_SEPARATOR); // Log date of test long start = System.currentTimeMillis(); SimpleDateFormat format = new SimpleDateFormat(); Date now = new Date(start); buffer.append("Test date : "); buffer.append(format.format(now)); buffer.append(LINE_SEPARATOR); // Input dir buffer.append("Input dir : "); buffer.append(this.inputDir); buffer.append(LINE_SEPARATOR); // Files buffer.append(" "); int[] maxFiles = (int[]) MAX_FILES.get(this.inputDir); buffer.append(maxFiles[0]); buffer.append(" java files to format..."); // Flush to console to show startup String firstBuffer = buffer.toString(); System.out.println(firstBuffer); // Output dir buffer.setLength(0); buffer.append("Output dir: "); buffer.append(OUTPUT_DIR); buffer.append(LINE_SEPARATOR); if (CLEAN) { buffer.append(" CLEANED"); buffer.append(LINE_SEPARATOR); } // Log dir if (LOG_FILE != null) { buffer.append("Log file : "); buffer.append(LOG_FILE); buffer.append(LINE_SEPARATOR); } // Write dir if (WRITE_DIR != null) { buffer.append("Write dir : "); buffer.append(WRITE_DIR); buffer.append(LINE_SEPARATOR); } // Comparison if (CAN_COMPARE) { if (!CLEAN) { buffer.append("Compare vs: "); File versionFile = new File(OUTPUT_DIR, "version.txt"); if (versionFile.exists()) { String fileContent = Util.fileContent(versionFile.getAbsolutePath()); if (TEMP_OUTPUT != null) { buffer.append(TEMP_OUTPUT); buffer.append(" on top of "); } buffer.append(fileContent); } else { buffer.append("???"); } } } else { buffer.append("Compare vs: none"); } buffer.append(LINE_SEPARATOR); // Write logs System.out.println(buffer.toString()); if (LOG_STREAM != null) { LOG_STREAM.println(firstBuffer); LOG_STREAM.println(buffer.toString()); LOG_STREAM.flush(); } } /* (non-Javadoc) * @see org.eclipse.jdt.core.tests.formatter.FormatterRegressionTests#tearDown() */ public void tearDown() throws Exception { // verify whether the max failures has been reached or not if (ASSERT_EQUALS_STRINGS && FAILURES != null) { ASSERT_EQUALS_STRINGS = FAILURES[COMPARISON_FAILURE].size() < MAX_FAILURES; } } /* (non-Javadoc) * @see org.eclipse.jdt.core.tests.formatter.FormatterRegressionTests#tearDownSuite() */ public void tearDownSuite() throws Exception { // Display time measures StringBuffer buffer1 = new StringBuffer(); if (CLEAN) { // buffer1.append(" cannot be done as the directory was cleaned!"); // buffer1.append(LINE_SEPARATOR); return; } else { buffer1.append("Time measures:"); buffer1.append(LINE_SEPARATOR); for (int i=0; i<FORMAT_REPEAT; i++) { buffer1.append(" - "+counterToString(i+1)).append(" format:").append(LINE_SEPARATOR); buffer1.append(" + elapsed = "+timeString(TIME_MEASURES.formatting[i])).append(LINE_SEPARATOR); buffer1.append(" + occurrences = "+TIME_MEASURES.occurences[i]).append(LINE_SEPARATOR); buffer1.append(" + null output = "+TIME_MEASURES.null_output[i]).append(LINE_SEPARATOR); } } buffer1.append(LINE_SEPARATOR); // Display stored failures int max = FAILURES.length; for (int i=0; i<max; i++) { List failures = FAILURES[i].failures; int size = failures.size(); if (size > 0) { buffer1.append(size); buffer1.append(" file"); if (size == 1) { buffer1.append(" has "); } else { buffer1.append("s have "); } buffer1.append(FAILURES[i]); buffer1.append('!'); buffer1.append(LINE_SEPARATOR); } } buffer1.append(LINE_SEPARATOR); StringBuffer buffer2 = new StringBuffer(LINE_SEPARATOR); for (int i=0; i<max; i++) { List failures = FAILURES[i].failures; int size = failures.size(); if (size > 0) { buffer2.append("List of file(s) with "); buffer2.append(FAILURES[i]); buffer2.append(':'); buffer2.append(LINE_SEPARATOR); for (int j=0; j<size; j++) { buffer2.append(" - "); buffer2.append(failures.get(j)); buffer2.append(LINE_SEPARATOR); } } } // Log failures System.out.println(buffer1.toString()); if (LOG_STREAM == null) { System.out.println(buffer2.toString()); } else { LOG_STREAM.print(buffer1.toString()); LOG_STREAM.print(buffer2.toString()); LOG_STREAM.close(); } // LOG_BUFFER.append(buffer1.toString()); // LOG_BUFFER.append(buffer2.toString()); // InputStream stream= new InputStream() { // private Reader reader= new StringReader(LOG_BUFFER.toString()); // public int read() throws IOException { // return this.reader.read(); // } // }; // if (LOG_RESOURCE.exists()) { // LOG_RESOURCE.setContents( // stream, // IResource.FORCE | IResource.KEEP_HISTORY, // null); // } else { // LOG_RESOURCE.create(stream, IResource.FORCE, null); // } } /* * Asserts that the given actual source (usually coming from a file content) is equal to the expected one. * Note that 'expected' is assumed to have the '\n' line separator. * The line separators in 'actual' are converted to '\n' before the comparison. */ protected void assertSourceEquals(String message, String expected, String actual) { if (expected == null) { assertNull(message, actual); return; } if (actual == null) { assertEquals(message, expected, null); return; } expected = Util.convertToIndependantLineDelimiter(expected); actual = Util.convertToIndependantLineDelimiter(actual); if (ASSERT_EQUALS_STRINGS) { assertEquals(message, expected, actual); } else { assertTrue(message, actual.equals(expected)); } } DefaultCodeFormatter codeFormatter() { DefaultCodeFormatter codeFormatter = new DefaultCodeFormatter(this.preferences, getDefaultCompilerOptions()); return codeFormatter; } void compareFormattedSource() throws IOException, Exception { String source = new String(org.eclipse.jdt.internal.compiler.util.Util.getFileCharContent(this.file, null)); String actualResult = null; try { // Format the source actualResult = runFormatter(codeFormatter(), source, CodeFormatter.K_COMPILATION_UNIT | CodeFormatter.F_INCLUDE_COMMENTS, 0, 0, source.length(), null, true); // Look for output to compare with File outputFile = new Path(OUTPUT_DIR.getPath()).append(this.path).toFile(); if (actualResult != null && FAILURES != null && this.canCompare) { try { String expectedResult = new String(org.eclipse.jdt.internal.compiler.util.Util.getFileCharContent(outputFile, null)); assertSourceEquals("Unexpected format output!", expectedResult, actualResult); } catch (FileNotFoundException fnfe) { this.failureIndex = FILE_NOT_FOUND_FAILURE; FAILURES[FILE_NOT_FOUND_FAILURE].failures.add(this.path); return; } catch (ComparisonFailure cf) { this.failureIndex = COMPARISON_FAILURE; throw cf; } catch (AssertionFailedError afe) { this.failureIndex = COMPARISON_FAILURE; throw afe; } } } catch (Exception e) { // System.err.println(e.getMessage()+" occurred in "+getName()); throw e; } finally { // Write file if (actualResult != null) { if (WRITE_DIR != null) { File writtenFile = new Path(WRITE_DIR.getPath()).append(this.path).toFile(); writtenFile.getParentFile().mkdirs(); Util.writeToFile(actualResult, writtenFile.getAbsolutePath()); } } } } private String counterToString(int count) { int reminder = count%10; StringBuffer buffer = new StringBuffer(); buffer.append(count); switch (reminder) { case 1: buffer.append("st"); break; case 2: buffer.append("nd"); break; case 3: buffer.append("rd"); break; default: buffer.append("th"); break; } return buffer.toString(); } private Map getDefaultCompilerOptions() { Map optionsMap = new HashMap(30); optionsMap.put(CompilerOptions.OPTION_LocalVariableAttribute, CompilerOptions.DO_NOT_GENERATE); optionsMap.put(CompilerOptions.OPTION_LineNumberAttribute, CompilerOptions.DO_NOT_GENERATE); optionsMap.put(CompilerOptions.OPTION_SourceFileAttribute, CompilerOptions.DO_NOT_GENERATE); optionsMap.put(CompilerOptions.OPTION_PreserveUnusedLocal, CompilerOptions.PRESERVE); optionsMap.put(CompilerOptions.OPTION_DocCommentSupport, CompilerOptions.DISABLED); optionsMap.put(CompilerOptions.OPTION_ReportMethodWithConstructorName, CompilerOptions.IGNORE); optionsMap.put(CompilerOptions.OPTION_ReportOverridingPackageDefaultMethod, CompilerOptions.IGNORE); optionsMap.put(CompilerOptions.OPTION_ReportOverridingMethodWithoutSuperInvocation, CompilerOptions.IGNORE); optionsMap.put(CompilerOptions.OPTION_ReportDeprecation, CompilerOptions.IGNORE); optionsMap.put(CompilerOptions.OPTION_ReportDeprecationInDeprecatedCode, CompilerOptions.DISABLED); optionsMap.put(CompilerOptions.OPTION_ReportDeprecationWhenOverridingDeprecatedMethod, CompilerOptions.DISABLED); optionsMap.put(CompilerOptions.OPTION_ReportHiddenCatchBlock, CompilerOptions.IGNORE); optionsMap.put(CompilerOptions.OPTION_ReportUnusedLocal, CompilerOptions.IGNORE); optionsMap.put(CompilerOptions.OPTION_ReportUnusedParameter, CompilerOptions.IGNORE); optionsMap.put(CompilerOptions.OPTION_ReportUnusedImport, CompilerOptions.IGNORE); optionsMap.put(CompilerOptions.OPTION_ReportSyntheticAccessEmulation, CompilerOptions.IGNORE); optionsMap.put(CompilerOptions.OPTION_ReportNoEffectAssignment, CompilerOptions.IGNORE); optionsMap.put(CompilerOptions.OPTION_ReportNonExternalizedStringLiteral, CompilerOptions.IGNORE); optionsMap.put(CompilerOptions.OPTION_ReportNoImplicitStringConversion, CompilerOptions.IGNORE); optionsMap.put(CompilerOptions.OPTION_ReportNonStaticAccessToStatic, CompilerOptions.IGNORE); optionsMap.put(CompilerOptions.OPTION_ReportIndirectStaticAccess, CompilerOptions.IGNORE); optionsMap.put(CompilerOptions.OPTION_ReportIncompatibleNonInheritedInterfaceMethod, CompilerOptions.IGNORE); optionsMap.put(CompilerOptions.OPTION_ReportUnusedPrivateMember, CompilerOptions.IGNORE); optionsMap.put(CompilerOptions.OPTION_ReportLocalVariableHiding, CompilerOptions.IGNORE); optionsMap.put(CompilerOptions.OPTION_ReportFieldHiding, CompilerOptions.IGNORE); optionsMap.put(CompilerOptions.OPTION_ReportPossibleAccidentalBooleanAssignment, CompilerOptions.IGNORE); optionsMap.put(CompilerOptions.OPTION_ReportEmptyStatement, CompilerOptions.IGNORE); optionsMap.put(CompilerOptions.OPTION_ReportAssertIdentifier, CompilerOptions.IGNORE); optionsMap.put(CompilerOptions.OPTION_ReportEnumIdentifier, CompilerOptions.IGNORE); optionsMap.put(CompilerOptions.OPTION_ReportUndocumentedEmptyBlock, CompilerOptions.IGNORE); optionsMap.put(CompilerOptions.OPTION_ReportUnnecessaryTypeCheck, CompilerOptions.IGNORE); optionsMap.put(CompilerOptions.OPTION_ReportInvalidJavadoc, CompilerOptions.IGNORE); optionsMap.put(CompilerOptions.OPTION_ReportInvalidJavadocTagsVisibility, CompilerOptions.PUBLIC); optionsMap.put(CompilerOptions.OPTION_ReportInvalidJavadocTags, CompilerOptions.DISABLED); optionsMap.put(CompilerOptions.OPTION_ReportMissingJavadocTagDescription, CompilerOptions.RETURN_TAG); optionsMap.put(CompilerOptions.OPTION_ReportInvalidJavadocTagsDeprecatedRef, CompilerOptions.DISABLED); optionsMap.put(CompilerOptions.OPTION_ReportInvalidJavadocTagsNotVisibleRef, CompilerOptions.DISABLED); optionsMap.put(CompilerOptions.OPTION_ReportMissingJavadocTags, CompilerOptions.IGNORE); optionsMap.put(CompilerOptions.OPTION_ReportMissingJavadocTagsVisibility, CompilerOptions.PUBLIC); optionsMap.put(CompilerOptions.OPTION_ReportMissingJavadocTagsOverriding, CompilerOptions.DISABLED); optionsMap.put(CompilerOptions.OPTION_ReportMissingJavadocComments, CompilerOptions.IGNORE); optionsMap.put(CompilerOptions.OPTION_ReportMissingJavadocCommentsVisibility, CompilerOptions.IGNORE); optionsMap.put(CompilerOptions.OPTION_ReportMissingJavadocCommentsOverriding, CompilerOptions.DISABLED); optionsMap.put(CompilerOptions.OPTION_ReportFinallyBlockNotCompletingNormally, CompilerOptions.IGNORE); optionsMap.put(CompilerOptions.OPTION_ReportUnusedDeclaredThrownException, CompilerOptions.IGNORE); optionsMap.put(CompilerOptions.OPTION_ReportUnusedDeclaredThrownExceptionWhenOverriding, CompilerOptions.DISABLED); optionsMap.put(CompilerOptions.OPTION_ReportUnqualifiedFieldAccess, CompilerOptions.IGNORE); optionsMap.put(CompilerOptions.OPTION_Compliance, CompilerOptions.VERSION_1_6); optionsMap.put(CompilerOptions.OPTION_TargetPlatform, CompilerOptions.VERSION_1_6); optionsMap.put(CompilerOptions.OPTION_TaskTags, ""); //$NON-NLS-1$ optionsMap.put(CompilerOptions.OPTION_TaskPriorities, ""); //$NON-NLS-1$ optionsMap.put(CompilerOptions.OPTION_TaskCaseSensitive, CompilerOptions.DISABLED); optionsMap.put(CompilerOptions.OPTION_ReportUnusedParameterWhenImplementingAbstract, CompilerOptions.DISABLED); optionsMap.put(CompilerOptions.OPTION_ReportUnusedParameterWhenOverridingConcrete, CompilerOptions.DISABLED); optionsMap.put(CompilerOptions.OPTION_ReportSpecialParameterHidingField, CompilerOptions.DISABLED); optionsMap.put(CompilerOptions.OPTION_MaxProblemPerUnit, String.valueOf(100)); optionsMap.put(CompilerOptions.OPTION_InlineJsr, CompilerOptions.DISABLED); optionsMap.put(CompilerOptions.OPTION_Source, CompilerOptions.VERSION_1_6); return optionsMap; } /* private boolean isExpectedFailure() { int length = EXPECTED_FAILURES.length; for (int i=0; i<length; i++) { IPath expectedFailure= EXPECTED_FAILURES[i]; if (this.path.toString().indexOf(expectedFailure.toString()) >= 0) { this.failureIndex = REFORMATTING_EXPECTED_FAILURE; FAILURES[REFORMATTING_EXPECTED_FAILURE].failures.add(this.path); return true; } } return false; } */ /* private boolean runFormatterWithoutComments(CodeFormatter codeFormatter, String source, int kind, int indentationLevel, int offset, int length, String lineSeparator) { DefaultCodeFormatterOptions preferencesWithoutComment = DefaultCodeFormatterOptions.getEclipseDefaultSettings(); preferencesWithoutComment.comment_format_line_comment = false; preferencesWithoutComment.comment_format_block_comment = false; preferencesWithoutComment.comment_format_javadoc_comment = false; DefaultCodeFormatter codeFormatterWithoutComment = new DefaultCodeFormatter(preferencesWithoutComment); TextEdit edit = codeFormatterWithoutComment.format(kind, source, offset, length, indentationLevel, lineSeparator);//$NON-NLS-1$ if (edit == null) return false; String initialResult = org.eclipse.jdt.internal.core.util.Util.editedString(source, edit); int count = 1; String result = initialResult; String previousResult = result; while (count++ < FORMAT_REPEAT) { edit = codeFormatterWithoutComment.format(kind, result, 0, result.length(), indentationLevel, lineSeparator);//$NON-NLS-1$ if (edit == null) return false; previousResult = result; result = org.eclipse.jdt.internal.core.util.Util.editedString(result, edit); } return previousResult.equals(result); } */ private boolean sourceHasCompilationErrors(String source) { CodeSnippetParsingUtil codeSnippetParsingUtil = new CodeSnippetParsingUtil(); codeSnippetParsingUtil.parseCompilationUnit(source.toCharArray(), getDefaultCompilerOptions(), true); if (codeSnippetParsingUtil.recordedParsingInformation != null) { CategorizedProblem[] problems = codeSnippetParsingUtil.recordedParsingInformation.problems; int length = problems == null ? 0 : problems.length; for (int i=0; i<length; i++) { if (((DefaultProblem)problems[i]).isError()) { return true; } } } return false; } String runFormatter(CodeFormatter codeFormatter, String source, int kind, int indentationLevel, int offset, int length, String lineSeparator, boolean repeat) { long timeStart = System.currentTimeMillis(); TextEdit edit = codeFormatter.format(kind, source, offset, length, indentationLevel, lineSeparator); if (FAILURES != null) { // Comparison has started TIME_MEASURES.formatting[0] += System.currentTimeMillis() - timeStart; TIME_MEASURES.occurences[0]++; if (edit == null) TIME_MEASURES.null_output[0]++; } if (edit == null) { if (sourceHasCompilationErrors(source)) { this.failureIndex = COMPILATION_ERRORS_FAILURE; FAILURES[COMPILATION_ERRORS_FAILURE].failures.add(this.path); return null; } this.failureIndex = NO_OUTPUT_FAILURE; throw new AssertionFailedError("Formatted source should not be null!"); } String initialResult = org.eclipse.jdt.internal.core.util.Util.editedString(source, edit); int count = 0; String result = initialResult; String previousResult = result; while (++count < FORMAT_REPEAT) { timeStart = System.currentTimeMillis(); edit = codeFormatter.format(kind, result, 0, result.length(), indentationLevel, lineSeparator); if (FAILURES != null) { // Comparison has started TIME_MEASURES.formatting[count] += System.currentTimeMillis() - timeStart; TIME_MEASURES.occurences[count]++; if (edit == null) TIME_MEASURES.null_output[count]++; } if (edit == null) return null; previousResult = result; result = org.eclipse.jdt.internal.core.util.Util.editedString(result, edit); } if (!previousResult.equals(result)) { if (FAILURES != null) { // Try to compare without leading spaces String trimmedExpected = ModelTestsUtil.trimLinesLeadingWhitespaces(previousResult); String trimmedActual= ModelTestsUtil.trimLinesLeadingWhitespaces(result); if (trimmedExpected.equals(trimmedActual)) { this.failureIndex = REFORMATTING_LEADING_FAILURE; FAILURES[REFORMATTING_LEADING_FAILURE].failures.add(this.path); return initialResult; } // Try to compare without spaces at all if (ModelTestsUtil.removeWhiteSpace(previousResult).equals(ModelTestsUtil.removeWhiteSpace(result))) { this.failureIndex = REFORMATTING_WHITESPACES_FAILURE; FAILURES[REFORMATTING_WHITESPACES_FAILURE].failures.add(this.path); return initialResult; } } /* // Try to see if the formatting also fails without comments if (!runFormatterWithoutComments(null, source, kind, indentationLevel, offset, length, lineSeparator)) { return initialResult; } // format without comments is OK => there's a problem with comment formatting String counterString = counterToString(count-1); assertSourceEquals(counterString+" formatting is different from first one!", previousResult, result); */ // if (!isExpectedFailure()) { String counterString = counterToString(count); try { assertSourceEquals(counterString+" formatting is different from first one!", previousResult, result); } catch (ComparisonFailure cf) { this.failureIndex = REFORMATTING_FAILURE; throw cf; } catch (AssertionFailedError afe) { this.failureIndex = REFORMATTING_FAILURE; throw afe; } // } } return initialResult; } /** * Returns a string to display the given time as a duration * formatted as: * <ul> * <li>"XXXms" if the duration is less than 0.1s (e.g. "543ms")</li> * <li>"X.YYs" if the duration is less than 1s (e.g. "5.43s")</li> * <li>"XX.Ys" if the duration is less than 1mn (e.g. "54.3s")</li> * <li>"XXmn XXs" if the duration is less than 1h (e.g. "54mn 3s")</li> * <li>"XXh XXmn XXs" if the duration is over than 1h (e.g. "5h 4mn 3s")</li> * </ul> * * @param time The time to format as a long. * @return The formatted string. */ public String timeString(long time) { NumberFormat format = NumberFormat.getInstance(); format.setMaximumFractionDigits(3); StringBuffer buffer = new StringBuffer(); if (time == 0) { // print nothing } else { long h = time / ONE_HOUR; if (h > 0) buffer.append(h).append("h "); //$NON-NLS-1$ long remaining = time % ONE_HOUR; long m = remaining / ONE_MINUTE; if (h > 0 || m > 0) buffer.append(m).append("mn "); //$NON-NLS-1$ remaining = remaining % ONE_MINUTE; if ((remaining % 1000) == 0) { buffer.append(remaining/1000); } else { buffer.append(format.format(remaining/1000.0)); } buffer.append("s"); //$NON-NLS-1$ } return buffer.toString(); } /* * Test to delete the output directory. * public void testDeleteOutputDir() throws IOException, Exception { Util.delete(this.inputDir); } /* * Test to fill the output directory with reference. */ public void testReference() throws IOException, Exception { // Dump the version // if (this.testIndex == 0) { // File versionFile = new Path(OUTPUT_DIR.getPath()).append("version.txt").toFile(); // OUTPUT_DIR.mkdirs(); // Util.writeToFile(JDT_CORE_VERSION, versionFile.getAbsolutePath()); // } // Get the source from file String source = new String(org.eclipse.jdt.internal.compiler.util.Util.getFileCharContent(this.file, null)); try { // Format the source TextEdit edit = codeFormatter().format(CodeFormatter.K_COMPILATION_UNIT | CodeFormatter.F_INCLUDE_COMMENTS, source, 0, source.length(), 0, null); // Write the result if (edit != null) { String formatResult = org.eclipse.jdt.internal.core.util.Util.editedString(source, edit); String inputPath = this.file.getPath().substring(this.inputDir.getPath().length()+1); File writtenFile = new Path(OUTPUT_DIR.getPath()).append(inputPath).toFile(); writtenFile.getParentFile().mkdirs(); Util.writeToFile(formatResult, writtenFile.getAbsolutePath()); } } catch (Exception ex) { // skip silently } } /* * Test to compare the formatter output with an existing file. */ public void testCompare() throws IOException, Exception { try { compareFormattedSource(); } catch (ComparisonFailure cf) { if (this.failureIndex == -1) { FAILURES[UNEXPECTED_FAILURE].failures.add(this.path); } else { FAILURES[this.failureIndex].failures.add(this.path); } throw cf; } catch (AssertionFailedError afe) { if (this.failureIndex == -1) { FAILURES[UNEXPECTED_FAILURE].failures.add(this.path); } else { FAILURES[this.failureIndex].failures.add(this.path); } throw afe; } catch (Exception ex) { FAILURES[UNEXPECTED_FAILURE].failures.add(this.path); throw ex; } } }