From 48a120cdb35746583049eb9ab953e070f99f539a Mon Sep 17 00:00:00 2001 From: Uli Schlachter Date: Fri, 23 Oct 2015 10:57:33 +0200 Subject: [PATCH 1/4] Utils: Add helper function for opening a BufferedWriter Signed-off-by: Uli Schlachter --- src/main/java/org/testng/internal/Utils.java | 46 +++++++++++++++----- 1 file changed, 35 insertions(+), 11 deletions(-) diff --git a/src/main/java/org/testng/internal/Utils.java b/src/main/java/org/testng/internal/Utils.java index bd97baf8dd..41bbad546a 100644 --- a/src/main/java/org/testng/internal/Utils.java +++ b/src/main/java/org/testng/internal/Utils.java @@ -200,17 +200,7 @@ private static void writeFile(@Nullable File outDir, String fileName, String sb, private static void writeFile(File outputFile, String sb, @Nullable String encoding, boolean append) { BufferedWriter fw = null; try { - if (!outputFile.exists()) { - outputFile.createNewFile(); - } - OutputStreamWriter osw= null; - if (null != encoding) { - osw = new OutputStreamWriter(new FileOutputStream(outputFile, append), encoding); - } - else { - osw = new OutputStreamWriter(new FileOutputStream(outputFile, append)); - } - fw = new BufferedWriter(osw); + fw = openWriter(outputFile, encoding, append); fw.write(sb); Utils.log("", 3, "Creating " + outputFile.getAbsolutePath()); @@ -236,6 +226,40 @@ private static void writeFile(File outputFile, String sb, @Nullable String encod } } + /** + * Open a BufferedWriter for the specified file. If output directory doesn't + * exist, it is created. If the output file exists, it is deleted. The output file is + * created in any case. + * @param outputDir output directory. If null, then current directory is used + * @param fileName file name + * @throws IOException if anything goes wrong while creating files. + */ + public static BufferedWriter openWriter(@Nullable String outputDir, String fileName) throws IOException { + String outDirPath= outputDir != null ? outputDir : ""; + File outDir= new File(outDirPath); + if (outDir.exists()) { + outDir.mkdirs(); + } + fileName = replaceSpecialCharacters(fileName); + File outputFile = new File(outDir, fileName); + outputFile.delete(); + return openWriter(outputFile, null, false); + } + + private static BufferedWriter openWriter(File outputFile, @Nullable String encoding, boolean append) throws IOException { + if (!outputFile.exists()) { + outputFile.createNewFile(); + } + OutputStreamWriter osw= null; + if (null != encoding) { + osw = new OutputStreamWriter(new FileOutputStream(outputFile, append), encoding); + } + else { + osw = new OutputStreamWriter(new FileOutputStream(outputFile, append)); + } + return new BufferedWriter(osw); + } + private static void ppp(String s) { Utils.log("Utils", 0, s); } From a9c69ddb24b95d680bdaab61c6c614bf58d73cb5 Mon Sep 17 00:00:00 2001 From: Uli Schlachter Date: Fri, 23 Oct 2015 11:00:39 +0200 Subject: [PATCH 2/4] SuiteHTMLReporter: Write files completely Before this, the SuiteHTMLReporter would write the chronologically list of methods one method at a time. After every method, the target file was opened and the new line was written. This behavior was introduced in commit 17c52218805e to fix an OOM error with many tests. This commit instead creates a BufferedWriter for the target file and writes the data directly to the target file, without re-opening it in a loop. For a random, non-representative test suite with about 2800 tests (the reason why I am looking into this), this speeds up the SuiteHTMLReporter from about 6.5 seconds to a quarter of a second. Signed-off-by: Uli Schlachter --- .../testng/reporters/SuiteHTMLReporter.java | 190 +++++++++--------- 1 file changed, 100 insertions(+), 90 deletions(-) diff --git a/src/main/java/org/testng/reporters/SuiteHTMLReporter.java b/src/main/java/org/testng/reporters/SuiteHTMLReporter.java index 3c75de06ac..e1a95cfdc9 100755 --- a/src/main/java/org/testng/reporters/SuiteHTMLReporter.java +++ b/src/main/java/org/testng/reporters/SuiteHTMLReporter.java @@ -14,6 +14,7 @@ import org.testng.internal.Utils; import org.testng.xml.XmlSuite; +import java.io.BufferedWriter; import java.io.File; import java.io.IOException; import java.lang.reflect.Method; @@ -346,101 +347,110 @@ private String dumpGroups(String[] groups) { private void generateMethodsChronologically(XmlSuite xmlSuite, ISuite suite, String outputFileName, boolean alphabetical) { - StringBuffer sb = new StringBuffer(); - - sb.append("

Methods run, sorted chronologically

"); - sb.append("

" + BEFORE + " means before, " + AFTER + " means after

"); - - long startDate = -1; - sb.append("
").append(suite.getName()).append("

"); - sb.append("(Hover the method name to see the test class name)

\n"); - Utils.writeFile(getOutputDirectory(xmlSuite), outputFileName, sb.toString()); - sb = null; //not needed anymore - - Collection invokedMethods = suite.getAllInvokedMethods(); - if (alphabetical) { - @SuppressWarnings({"unchecked"}) - Comparator alphabeticalComparator = new Comparator(){ - @Override - public int compare(Object o1, Object o2) { - IInvokedMethod m1 = (IInvokedMethod) o1; - IInvokedMethod m2 = (IInvokedMethod) o2; - return m1.getTestMethod().getMethodName().compareTo(m2.getTestMethod().getMethodName()); - } - }; - Collections.sort((List) invokedMethods, alphabeticalComparator); - } + BufferedWriter bw = null; + try { + bw = Utils.openWriter(getOutputDirectory(xmlSuite), outputFileName); + + bw.append("

Methods run, sorted chronologically

"); + bw.append("

" + BEFORE + " means before, " + AFTER + " means after

"); + + long startDate = -1; + bw.append("
").append(suite.getName()).append("

"); + bw.append("(Hover the method name to see the test class name)

\n"); + + Collection invokedMethods = suite.getAllInvokedMethods(); + if (alphabetical) { + @SuppressWarnings({"unchecked"}) + Comparator alphabeticalComparator = new Comparator(){ + @Override + public int compare(Object o1, Object o2) { + IInvokedMethod m1 = (IInvokedMethod) o1; + IInvokedMethod m2 = (IInvokedMethod) o2; + return m1.getTestMethod().getMethodName().compareTo(m2.getTestMethod().getMethodName()); + } + }; + Collections.sort((List) invokedMethods, alphabeticalComparator); + } - SimpleDateFormat format = new SimpleDateFormat("yy/MM/dd HH:mm:ss"); - StringBuffer table = new StringBuffer(); - boolean addedHeader = false; - for (IInvokedMethod iim : invokedMethods) { - ITestNGMethod tm = iim.getTestMethod(); - table.setLength(0); - if (!addedHeader) { - table.append("\n") - .append("") - .append("") - .append("") - .append("") - .append("") - .append("") - .append("") - .append("") - .append("") - .append("") - .append("") - .append("\n"); - addedHeader = true; + SimpleDateFormat format = new SimpleDateFormat("yy/MM/dd HH:mm:ss"); + boolean addedHeader = false; + for (IInvokedMethod iim : invokedMethods) { + ITestNGMethod tm = iim.getTestMethod(); + if (!addedHeader) { + bw.append("
TimeDelta (ms)Suite
configuration
Test
configuration
Class
configuration
Groups
configuration
Method
configuration
Test
method
ThreadInstances
\n") + .append("") + .append("") + .append("") + .append("") + .append("") + .append("") + .append("") + .append("") + .append("") + .append("") + .append("") + .append("\n"); + addedHeader = true; + } + String methodName = tm.toString(); + boolean bc = tm.isBeforeClassConfiguration(); + boolean ac = tm.isAfterClassConfiguration(); + boolean bt = tm.isBeforeTestConfiguration(); + boolean at = tm.isAfterTestConfiguration(); + boolean bs = tm.isBeforeSuiteConfiguration(); + boolean as = tm.isAfterSuiteConfiguration(); + boolean bg = tm.isBeforeGroupsConfiguration(); + boolean ag = tm.isAfterGroupsConfiguration(); + boolean setUp = tm.isBeforeMethodConfiguration(); + boolean tearDown = tm.isAfterMethodConfiguration(); + boolean isClassConfiguration = bc || ac; + boolean isGroupsConfiguration = bg || ag; + boolean isTestConfiguration = bt || at; + boolean isSuiteConfiguration = bs || as; + boolean isSetupOrTearDown = setUp || tearDown; + String configurationClassMethod = isClassConfiguration ? (bc ? BEFORE : AFTER) + methodName : SP; + String configurationTestMethod = isTestConfiguration ? (bt ? BEFORE : AFTER) + methodName : SP; + String configurationGroupsMethod = isGroupsConfiguration ? (bg ? BEFORE : AFTER) + methodName : SP; + String configurationSuiteMethod = isSuiteConfiguration ? (bs ? BEFORE : AFTER) + methodName : SP; + String setUpOrTearDownMethod = isSetupOrTearDown ? (setUp ? BEFORE : AFTER) + methodName : SP; + String testMethod = tm.isTest() ? methodName : SP; + + StringBuffer instances = new StringBuffer(); + for (long o : tm.getInstanceHashCodes()) { + instances.append(o).append(" "); + } + + if (startDate == -1) { + startDate = iim.getDate(); + } + String date = format.format(iim.getDate()); + bw.append("") + .append(" ") + .append(" ") + .append(td(configurationSuiteMethod)) + .append(td(configurationTestMethod)) + .append(td(configurationClassMethod)) + .append(td(configurationGroupsMethod)) + .append(td(setUpOrTearDownMethod)) + .append(td(testMethod)) + .append(" ") + .append(" ") + .append("\n") + ; } - String methodName = tm.toString(); - boolean bc = tm.isBeforeClassConfiguration(); - boolean ac = tm.isAfterClassConfiguration(); - boolean bt = tm.isBeforeTestConfiguration(); - boolean at = tm.isAfterTestConfiguration(); - boolean bs = tm.isBeforeSuiteConfiguration(); - boolean as = tm.isAfterSuiteConfiguration(); - boolean bg = tm.isBeforeGroupsConfiguration(); - boolean ag = tm.isAfterGroupsConfiguration(); - boolean setUp = tm.isBeforeMethodConfiguration(); - boolean tearDown = tm.isAfterMethodConfiguration(); - boolean isClassConfiguration = bc || ac; - boolean isGroupsConfiguration = bg || ag; - boolean isTestConfiguration = bt || at; - boolean isSuiteConfiguration = bs || as; - boolean isSetupOrTearDown = setUp || tearDown; - String configurationClassMethod = isClassConfiguration ? (bc ? BEFORE : AFTER) + methodName : SP; - String configurationTestMethod = isTestConfiguration ? (bt ? BEFORE : AFTER) + methodName : SP; - String configurationGroupsMethod = isGroupsConfiguration ? (bg ? BEFORE : AFTER) + methodName : SP; - String configurationSuiteMethod = isSuiteConfiguration ? (bs ? BEFORE : AFTER) + methodName : SP; - String setUpOrTearDownMethod = isSetupOrTearDown ? (setUp ? BEFORE : AFTER) + methodName : SP; - String testMethod = tm.isTest() ? methodName : SP; - - StringBuffer instances = new StringBuffer(); - for (long o : tm.getInstanceHashCodes()) { - instances.append(o).append(" "); + bw.append("
TimeDelta (ms)Suite
configuration
Test
configuration
Class
configuration
Groups
configuration
Method
configuration
Test
method
ThreadInstances
").append(date).append("").append(Long.toString(iim.getDate() - startDate)).append("").append(tm.getId()).append("").append(instances).append("
\n"); + } catch (IOException e) { + Utils.log("[SuiteHTMLReporter]", 1, "Error writing to " + outputFileName + ": " + e.getMessage()); + } finally { + try { + if (bw != null) { + bw.close(); + } } - - if (startDate == -1) { - startDate = iim.getDate(); + catch (IOException e) { + ; // ignore } - String date = format.format(iim.getDate()); - table.append("") - .append(" ").append(date).append(" ") - .append(" ").append(iim.getDate() - startDate).append(" ") - .append(td(configurationSuiteMethod)) - .append(td(configurationTestMethod)) - .append(td(configurationClassMethod)) - .append(td(configurationGroupsMethod)) - .append(td(setUpOrTearDownMethod)) - .append(td(testMethod)) - .append(" ").append(tm.getId()).append(" ") - .append(" ").append(instances).append(" ") - .append("\n") - ; - Utils.appendToFile(getOutputDirectory(xmlSuite), outputFileName, table.toString()); } - Utils.appendToFile(getOutputDirectory(xmlSuite), outputFileName, "\n"); } /** From 83f1c10279ecbe6cbcf4fa3ff63e7f0830c18749 Mon Sep 17 00:00:00 2001 From: Uli Schlachter Date: Fri, 23 Oct 2015 11:04:55 +0200 Subject: [PATCH 3/4] Remove Utils.appendToFile() The last commit removed the only user of this function. Signed-off-by: Uli Schlachter --- src/main/java/org/testng/internal/Utils.java | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/src/main/java/org/testng/internal/Utils.java b/src/main/java/org/testng/internal/Utils.java index 41bbad546a..741a812ff4 100644 --- a/src/main/java/org/testng/internal/Utils.java +++ b/src/main/java/org/testng/internal/Utils.java @@ -149,19 +149,6 @@ public static void writeFile(@Nullable String outputDir, String fileName, String writeFile(outDir, fileName, sb, null, false /* don't append */); } - /** - * Appends contents of the string to the specified file. If output directory/file don't - * exist, they are created. - * @param outputDir output directory. If null, then current directory is used - * @param fileName file name - * @param sb string to be appended to file - */ - public static void appendToFile(@Nullable String outputDir, String fileName, String sb) { - String outDirPath= outputDir != null ? outputDir : ""; - File outDir= new File(outDirPath); - writeFile(outDir, fileName, sb, null, true /* append */); - } - /** * Writes the content of the sb string to the file named filename in outDir. If * outDir does not exist, it is created. From e353be223c97f152cb3eb1b6df65200eeed7021f Mon Sep 17 00:00:00 2001 From: Uli Schlachter Date: Sat, 24 Oct 2015 08:19:21 +0200 Subject: [PATCH 4/4] fixup! SuiteHTMLReporter: Write files completely --- .../org/testng/reporters/SuiteHTMLReporter.java | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/src/main/java/org/testng/reporters/SuiteHTMLReporter.java b/src/main/java/org/testng/reporters/SuiteHTMLReporter.java index e1a95cfdc9..fff8ddffaa 100755 --- a/src/main/java/org/testng/reporters/SuiteHTMLReporter.java +++ b/src/main/java/org/testng/reporters/SuiteHTMLReporter.java @@ -347,10 +347,7 @@ private String dumpGroups(String[] groups) { private void generateMethodsChronologically(XmlSuite xmlSuite, ISuite suite, String outputFileName, boolean alphabetical) { - BufferedWriter bw = null; - try { - bw = Utils.openWriter(getOutputDirectory(xmlSuite), outputFileName); - + try (BufferedWriter bw = Utils.openWriter(getOutputDirectory(xmlSuite), outputFileName)) { bw.append("

Methods run, sorted chronologically

"); bw.append("

" + BEFORE + " means before, " + AFTER + " means after

"); @@ -441,15 +438,6 @@ public int compare(Object o1, Object o2) { bw.append("\n"); } catch (IOException e) { Utils.log("[SuiteHTMLReporter]", 1, "Error writing to " + outputFileName + ": " + e.getMessage()); - } finally { - try { - if (bw != null) { - bw.close(); - } - } - catch (IOException e) { - ; // ignore - } } }