Mercurial > hg > ThermostatQA
changeset 173:6b6ac47a811f
Added new reporter (generating report from test logs)
slightly modified existing templates and stylesheet (foldable details)
author | Zdenek Zambersky <zzambers@redhat.com> |
---|---|
date | Thu, 19 Mar 2015 12:44:59 +0100 |
parents | 661ca2d7795f |
children | 4d8930e19c50 |
files | Makefile src/org/thermostat/qa/reporter/ResultsGraphGenerator.java src/org/thermostat/qa2/framework/ThermostatQAConfig.java src/org/thermostat/qa2/framework/utils/FileUtilities.java src/org/thermostat/qa2/reporter/Generator.java src/org/thermostat/qa2/reporter/LogParser.java src/org/thermostat/qa2/reporter/Reporter.java src/org/thermostat/qa2/reporter/result/TestClassResult.java src/org/thermostat/qa2/reporter/result/TestMethodResult.java src/org/thermostat/qa2/reporter/result/TestRunResult.java templates/graph.html templates/hist.html templates/index.html templates/log.html templates/style.css |
diffstat | 15 files changed, 910 insertions(+), 31 deletions(-) [+] |
line wrap: on
line diff
--- a/Makefile Thu Mar 19 12:25:06 2015 +0100 +++ b/Makefile Thu Mar 19 12:44:59 2015 +0100 @@ -187,27 +187,16 @@ perl $(JAPI_COMPILANCE_CHECKER_DIR)/japi-compliance-checker.pl -lib Thermostat -old $(BUILD_DIR)/thermostat-other.xml -new $(BUILD_DIR)/thermostat-tested.xml || true #### reports & javadoc #### - -report: $(CLASSES_DIR) flotr javadoc + +report: $(CLASSES_DIR) flotr mkdir -p $(REPORT_DIR) cp -u $(TEMPLATE_DIR)/style.css $(REPORT_DIR)/style.css cp -r $(FLOTR_DIR) $(REPORT_DIR) - $(JAVA) -cp $(CLASSES_DIR) org.thermostat.qa.reporter.Reporter -template-dir=$(TEMPLATE_DIR) -log-dir=$(LOGS_DIR) -report-dir=$(REPORT_DIR) -date=$(DATE) -tests="$(TESTS)" - sed -i 's/ignored/not applicable/g' $(REPORT_DIR)/log_$(DATE).html - sed -i 's/IGNORED/NOT APPLICABLE/g' $(REPORT_DIR)/log_$(DATE).html - links -dump $(REPORT_DIR)/index.html 2>/dev/null > $(REPORT_DIR)/$(DAILY_REPORT_NAME) || \ - w3m -dump $(REPORT_DIR)/index.html 2>/dev/null > $(REPORT_DIR)/$(DAILY_REPORT_NAME) || \ - lynx -dump $(REPORT_DIR)/index.html -force_html 2>/dev/null > $(REPORT_DIR)/$(DAILY_REPORT_NAME) - echo "" >> $(REPORT_DIR)/$(DAILY_REPORT_NAME) - echo "" >> $(REPORT_DIR)/$(DAILY_REPORT_NAME) - links -dump $(REPORT_DIR)/log_$(DATE).html 2>/dev/null >> $(REPORT_DIR)/$(DAILY_REPORT_NAME) || \ - w3m -dump $(REPORT_DIR)/log_$(DATE).html 2>/dev/null >> $(REPORT_DIR)/$(DAILY_REPORT_NAME) || \ - lynx -dump $(REPORT_DIR)/log_$(DATE).html -force_html 2>/dev/null >> $(REPORT_DIR)/$(DAILY_REPORT_NAME) - echo "" >> $(REPORT_DIR)/$(DAILY_REPORT_NAME) - echo "" >> $(REPORT_DIR)/$(DAILY_REPORT_NAME) - links -dump $(REPORT_DIR)/hist_10.html 2>/dev/null >> $(REPORT_DIR)/$(DAILY_REPORT_NAME) || \ - w3m -dump $(REPORT_DIR)/hist_10.html 2>/dev/null >> $(REPORT_DIR)/$(DAILY_REPORT_NAME) || \ - lynx -dump $(REPORT_DIR)/hist_10.html -force_html 2>/dev/null >> $(REPORT_DIR)/$(DAILY_REPORT_NAME) + $(JAVA) -cp $(CLASSES_DIR) \ + -Dthermostat.version=$(THERMOSTAT_VERSION) \ + -Dlogs.path=$(LOGS_DIR) \ + -Dreports.path=$(REPORT_DIR) \ + org.thermostat.qa2.reporter.Reporter javadoc: mkdir -p $(JAVADOC_DIR)
--- a/src/org/thermostat/qa/reporter/ResultsGraphGenerator.java Thu Mar 19 12:25:06 2015 +0100 +++ b/src/org/thermostat/qa/reporter/ResultsGraphGenerator.java Thu Mar 19 12:44:59 2015 +0100 @@ -84,7 +84,7 @@ * @param string * @throws IOException */ - private void generateGraph(TestResult testResult, Dimension graphSize, String fileName) throws IOException { + public void generateGraph(TestResult testResult, Dimension graphSize, String fileName) throws IOException { setupGeometry(graphSize); BufferedImage bitmap = createBitmap(); setGraphics(bitmap);
--- a/src/org/thermostat/qa2/framework/ThermostatQAConfig.java Thu Mar 19 12:25:06 2015 +0100 +++ b/src/org/thermostat/qa2/framework/ThermostatQAConfig.java Thu Mar 19 12:44:59 2015 +0100 @@ -47,6 +47,8 @@ public static int webStoragePort = 8080; public static String gnomeKeyringConfigDir; public static String backupDir; + public static String logsDir; + public static String reportDir; public static Login mongoLogin = new Login("mongodevuser", "mongodevpassword"); public static Login agentLogin = new Login("agent-tester", "heslo1"); @@ -79,6 +81,9 @@ gnomeKeyringConfigDir = System.getProperty("gnome-keyring.config.path"); backupDir = System.getProperty("backup.path"); + + logsDir = System.getProperty("logs.path"); + reportDir = System.getProperty("reports.path"); } static String getPatternsDir() {
--- a/src/org/thermostat/qa2/framework/utils/FileUtilities.java Thu Mar 19 12:25:06 2015 +0100 +++ b/src/org/thermostat/qa2/framework/utils/FileUtilities.java Thu Mar 19 12:44:59 2015 +0100 @@ -33,8 +33,10 @@ import java.io.BufferedReader; import java.io.File; import java.io.FileNotFoundException; +import java.io.FileOutputStream; import java.io.FileReader; import java.io.IOException; +import java.io.PrintStream; import java.util.ArrayList; import java.util.List; import java.util.logging.Level; @@ -94,4 +96,22 @@ } } + public static void printLineListToFile(String file, List<String> list) throws FileNotFoundException { + FileOutputStream fos = null; + PrintStream ps = null; + + try { + fos = new FileOutputStream(file); + ps = new PrintStream(fos); + for (String s : list) { + ps.println(s); + } + ps.flush(); + } finally { + if (ps != null) { + ps.close(); + } + } + } + }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/org/thermostat/qa2/reporter/Generator.java Thu Mar 19 12:44:59 2015 +0100 @@ -0,0 +1,355 @@ +/* + ThermostatQA - test framework for Thermostat Monitoring Tool + + Copyright 2015 Red Hat, Inc. + + This file is part of ThermostatQA + + ThermostatQA is distributed under the GNU General Public License, + version 2 or any later version (with a special exception described + below, commonly known as the "Classpath Exception"). + + A copy of GNU General Public License (GPL) is included in this + distribution, in the file COPYING. + + Linking ThermostatQA code with other modules is making a combined work + based on ThermostatQA. Thus, the terms and conditions of the GPL + cover the whole combination. + + As a special exception, the copyright holders of ThermostatQA give you + permission to link this code with independent modules to produce an + executable, regardless of the license terms of these independent + modules, and to copy and distribute the resulting executable under + terms of your choice, provided that you also meet, for each linked + independent module, the terms and conditions of the license of that + module. An independent module is a module which is not derived from + or based on ThermostatQA code. If you modify ThermostatQA, you may + extend this exception to your version of the software, but you are + not obligated to do so. If you do not wish to do so, delete this + exception statement from your version. + */ +package org.thermostat.qa2.reporter; + +import org.thermostat.qa2.reporter.result.TestMethodResult; +import org.thermostat.qa2.reporter.result.TestClassResult; +import org.thermostat.qa2.reporter.result.TestRunResult; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.thermostat.qa2.framework.TestRunner; +import org.thermostat.qa2.framework.TestResult; +import org.thermostat.qa2.framework.utils.FileUtilities; + +/** + * + * @author Zdeněk Žamberský + */ +public class Generator { + + public static final String resultsUrlPattern = "${RESULTS_URL}"; + public static final String datePattern = "${DATE}"; + public static final String testCountPattern = "${TEST_COUNT}"; + + public static final String passedPattern = "${PASSED}"; + public static final String failedPattern = "${FAILED}"; + public static final String errorPattern = "${ERROR}"; + public static final String ignoredPattern = "${IGNORED}"; + + public static final String osNamePattern = "${OS_NAME}"; + public static final String osVersionPattern = "${OS_VER}"; + public static final String osArchPattern = "${OS_ARCH}"; + + public static final String javaVersionPattern = "${JAVA_VERSION}"; + public static final String vmNamePattern = "${VM_NAME}"; + public static final String vmVersionPattern = "${VM_VERSION}"; + + public static final String sumaryPattern = "${SUMMARY}"; + public static final String resultsPattern = "${RESULTS}"; + public static final String tableDataPattern = "${TABLE_DATA}"; + + public static final String graphDataNonePattern = "${GRAPH_DATA_NONE}"; + public static final String graphDataPassedPattern = "${GRAPH_DATA_PASSED}"; + public static final String graphDataFailedPattern = "${GRAPH_DATA_FAILED}"; + public static final String graphDataErrorPattern = "${GRAPH_DATA_ERROR}"; + public static final String graphDataIgnoredPattern = "${GRAPH_DATA_IGNORED}"; + + public static final String indexPageTemplate = "templates/index.html"; + public static final String logPageTemplate = "templates/log.html"; + public static final String historyPageTemplate = "templates/hist.html"; + public static final String graphPageTemplate = "templates/graph.html"; + + //////////////////// + //// INDEX PAGE //// + //////////////////// + public static void generateIndexPage(List<String> output, TestRunResult run) throws IOException { + Map<String, List<String>> replacements = new HashMap(); + addReplacementToMap(replacements, resultsUrlPattern, "log_" + run.date + ".xhtml"); + addReplacementToMap(replacements, datePattern, run.date); + addReplacementToMap(replacements, passedPattern, Integer.toString(run.passedCount)); + addReplacementToMap(replacements, failedPattern, Integer.toString(run.failedCount)); + addReplacementToMap(replacements, errorPattern, Integer.toString(run.errorCount)); + addReplacementToMap(replacements, ignoredPattern, Integer.toString(run.ignoredCount)); + addReplacementToMap(replacements, osNamePattern, System.getProperty("os.name")); + addReplacementToMap(replacements, osVersionPattern, System.getProperty("os.version")); + addReplacementToMap(replacements, osArchPattern, System.getProperty("os.arch")); + addReplacementToMap(replacements, javaVersionPattern, System.getProperty("java.version")); + addReplacementToMap(replacements, vmNamePattern, System.getProperty("java.vm.name")); + addReplacementToMap(replacements, vmVersionPattern, System.getProperty("java.vm.version")); + List<String> template = FileUtilities.getLineListFromFile(indexPageTemplate); + replacePatterns(template, output, replacements); + } + + ////////////////// + //// LOG PAGE //// + ////////////////// + public static void generateLogPage(List<String> output, TestRunResult run) throws IOException { + List<String> dateText = new ArrayList(); + dateText.add(run.date); + List<String> summaryText = new ArrayList(); + generateLogSummary(summaryText, run); + List<String> resultsText = new ArrayList(); + generateLogTable(resultsText, run); + Map<String, List<String>> replacements = new HashMap(); + replacements.put(datePattern, dateText); + replacements.put(sumaryPattern, summaryText); + replacements.put(resultsPattern, resultsText); + List<String> template = FileUtilities.getLineListFromFile(logPageTemplate); + replacePatterns(template, output, replacements); + } + + public static void generateLogSummary(List<String> output, TestRunResult run) { + for (TestClassResult test : run.results) { + output.add("<tr><td><a href='#" + test.name + "'>" + test.name + "</a></td></tr>"); + } + } + + public static void generateLogTable(List<String> output, TestRunResult run) { + for (TestClassResult test : run.results) { + String testName = test.name; + output.add("<tr id='" + testName + "' ><td class='table-header' colspan='2'>" + testName + "</td></tr>"); + for (TestMethodResult method : test.methods) { + TestResult result = method.result; + String resultString = result.toString(); + String resultLine = "<tr id='" + testName + "." + method.name + "' >"; + resultLine += result == TestResult.PASSED ? "<td>" : "<td class='" + resultString.toLowerCase() + "-header'" + ">"; + resultLine += "<span class='test-name-prefix'>" + testName + ".</span><span class='test-name-postfix'>" + method.name + "</span>"; + if (method.reason != null) { + resultLine += "<span class='test-name-postfix'>: </span><small class='reason'>" + method.reason + "</small>"; + } + resultLine += "</td><td class='" + resultString.toLowerCase() + "-text'>" + resultString + "</td></tr>"; + output.add(resultLine); + List<String> stackTrace = method.stackTrace; + if (stackTrace != null) { + output.add("<tr><td>"); + output.add("<input id=\"log-toggle-" + testName + "-" + method.name + "\" type=\"checkbox\" class=\"log-toggle\" />"); + output.add("<label for=\"log-toggle-" + testName + "-" + method.name + "\"></label><br />"); + output.add("<div>"); + output.add("<h5>stack trace:</h5>"); + output.add("<pre class='stack-trace'><![CDATA[" + (stackTrace.size() > 0 ? stackTrace.get(0) : "")); + for (int i = 1; i < stackTrace.size(); ++i) { + output.add(stackTrace.get(i)); + } + output.add("]]></pre>"); + output.add("<h5>log:</h5>"); + List<String> log = method.getLog(); + output.add("<pre class='stack-trace'><![CDATA[" + (log.size() > 0 ? log.get(0) : "")); + for (int i = 1; i < log.size(); ++i) { + output.add(log.get(i)); + } + output.add("]]></pre>"); + output.add("</div>"); + output.add("</td></tr>"); + } + } + output.add("<tr><td class='error-text' colspan='2'>SUMMARY: " + testName + " total: " + test.methods.size() + " passed: " + test.passedCount + " failed: " + test.failedCount + " error: " + test.errorCount + " not applicable: " + test.ignoredCount + " duration: " + test.duration + "</td></tr>"); + output.add("<tr><td colspan='2'> </td></tr>"); + } + } + + ////////////////////// + //// HISTORY PAGE //// + ////////////////////// + public static void generateHistoryPage(List<String> output, List<TestRunResult> runs, String resultsType, int count, boolean onlyFail) throws IOException { + List<String> resultTypeText = new ArrayList(); + resultTypeText.add(resultsType); + List<String> historyTableText = new ArrayList(); + generateHistoryTable(historyTableText, runs, count, onlyFail); + Map<String, List<String>> replacements = new HashMap(); + replacements.put(resultsPattern, resultTypeText); + replacements.put(tableDataPattern, historyTableText); + List<String> template = FileUtilities.getLineListFromFile(historyPageTemplate); + replacePatterns(template, output, replacements); + } + + public static void generateHistoryTable(List<String> output, List<TestRunResult> runs, int count, boolean onlyFail) { + int size = runs.size(); + int start = Math.max(0, size - count); + TestRunResult lastRun = runs.get(size - 1); + output.add("<tr><td class='group-id'>Test name/Date:</td>"); + for (int i = start; i < size; ++i) { + String name = runs.get(i).date; + int index = name.indexOf("-"); + String year = name.substring(0, index); + String date = name.substring(index + 1, name.length()); + output.add("<td>" + year + "<br />" + date + "</td>"); + } + output.add("</tr>"); + List<TestClassResult> lastResults = lastRun.results; + List<TestClassResult> testHistory = new ArrayList(); + for (TestClassResult result : lastResults) { + String name = result.name; + for (int i = start; i < size; ++i) { + Map<String, TestClassResult> testMap = runs.get(i).resultsMap; + testHistory.add(testMap.get(name)); + } + generateHistoryTableRow(output, runs, testHistory, count, onlyFail); + testHistory.clear(); + } + } + + public static void generateHistoryTableRow(List<String> output, List<TestRunResult> runs, List<TestClassResult> testHistory, int count, boolean onlyFail) { + int size = testHistory.size(); + TestRunResult lastRun = runs.get(size - 1); + TestClassResult current = testHistory.get(size - 1); + List<TestMethodResult> allMethods = new ArrayList(); + for (TestMethodResult method : current.methods) { + String name = method.getName(); + boolean fail = false; + for (TestClassResult previous : testHistory) { + if (previous != null) { + TestMethodResult previousMethod = previous.getMethod(name); + if (previousMethod != null && previousMethod.getResult() != TestResult.PASSED) { + fail = true; + } + allMethods.add(previousMethod); + } else { + allMethods.add(null); + } + } + if (!onlyFail || fail) { + String lastTestFile = "log_" + lastRun.date + ".xhtml"; + String methodref = "#" + current.name + "." + method.name; + output.add("<tr><td>" + current.name + ".<a href='" + lastTestFile + methodref + "'>" + method.getName() + "</a></td>"); + int i = 0; + for (TestMethodResult printedMethod : allMethods) { + if (printedMethod != null) { + TestResult result = printedMethod.getResult(); + String resultString = result.toString().toLowerCase(); + String printedResultString = result == TestResult.PASSED ? "ok" : result == TestResult.FAILED ? "fail" : resultString; + output.add("<td class='" + resultString + "-header'><a href='" + "log_" + runs.get(i).date + ".xhtml" + methodref + "'>" + printedResultString + "</a></td>"); + } else { + output.add("<td> </td>"); + } + ++i; + } + output.add("</tr>"); + } + allMethods.clear(); + } + } + + //////////////////// + //// GRAPH PAGE //// + //////////////////// + public static void generateGraphPage(List<String> output, List<TestRunResult> runs, int count) throws IOException { + List<String> testCountText = new ArrayList(); + testCountText.add(Integer.toString(count)); + List<String> passedDataText = new ArrayList(); + generateGraphData(passedDataText, runs, TestResult.PASSED, count); + List<String> failedDataText = new ArrayList(); + generateGraphData(failedDataText, runs, TestResult.FAILED, count); + List<String> errorDataText = new ArrayList(); + generateGraphData(errorDataText, runs, TestResult.ERROR, count); + List<String> ignorredDataText = new ArrayList(); + generateGraphData(ignorredDataText, runs, TestResult.IGNORED, count); + List<String> noneDataText = new ArrayList(); + generateGraphData(noneDataText, runs, null, count); + List<String> tableText = new ArrayList(); + generateGraphTable(tableText, runs, count); + Map<String, List<String>> replacements = new HashMap(); + replacements.put(testCountPattern, testCountText); + replacements.put(graphDataPassedPattern, passedDataText); + replacements.put(graphDataFailedPattern, failedDataText); + replacements.put(graphDataErrorPattern, errorDataText); + replacements.put(graphDataIgnoredPattern, ignorredDataText); + replacements.put(graphDataNonePattern, noneDataText); + replacements.put(tableDataPattern, tableText); + List<String> template = FileUtilities.getLineListFromFile(graphPageTemplate); + replacePatterns(template, output, replacements); + } + + public static void generateGraphTable(List<String> output, List<TestRunResult> results, int count) { + int size = results.size(); + int start = Math.max(0, size - count); + for (int i = start; i < results.size(); ++i) { + TestRunResult result = results.get(i); + output.add("<tr><td>" + i + "</td><td><a href='log_" + result.date + ".xhtml'> " + result.date + " </a></td>"); + output.add("<td style='color:#006000;text-align:right;'>" + result.passedCount + "</td>"); + output.add("<td style='color:#800000;text-align:right;'>" + result.failedCount + "</td>"); + output.add("<td style='color:#000080;text-align:right;'>" + result.errorCount + "</td>"); + output.add("<td style='color:#606060;text-align:right;'>" + result.ignoredCount + "</td>"); + output.add("</tr>"); + } + } + + public static void generateGraphData(List<String> output, List<TestRunResult> results, TestResult result, int count) { + int size = results.size(); + int start = Math.max(0, size - count); + for (int i = start; i < results.size(); ++i) { + TestRunResult run = results.get(i); + int targetResultCount = result == TestResult.PASSED ? run.passedCount : result == TestResult.FAILED ? run.failedCount : result == TestResult.ERROR ? run.errorCount : result == TestResult.IGNORED ? run.ignoredCount : 0; + output.add("[" + (i + 1) + ", " + targetResultCount + "],"); + } + } + + //////////////// + //// COMMON //// + //////////////// + public static void replacePatterns(List<String> template, List<String> output, Map<String, List<String>> patterns) { + int patternsCount = patterns.size(); + Map.Entry<String, List<String>>[] entries = new Map.Entry[patternsCount]; + patterns.entrySet().toArray(entries); + for (String line : template) { + String restOfLine = line; + String toPrint = null; + Map.Entry<String, List<String>> entryFound; + do { + entryFound = null; + int index = -1; + for (int i = 0; i < patternsCount; ++i) { + Map.Entry<String, List<String>> entry = entries[i]; + String pattern = entry.getKey(); + int foundIndex = restOfLine.indexOf(pattern); + if (foundIndex >= 0 && (entryFound == null || foundIndex < index)) { + index = foundIndex; + entryFound = entry; + } + } + if (entryFound != null) { + String pattern = entryFound.getKey(); + List<String> replacement = entryFound.getValue(); + int replacementSize = replacement.size(); + toPrint = line.substring(0, index); + for (int j = 0; j < replacementSize - 1; ++j) { + String replacementLine = replacement.get(j); + output.add(toPrint == null ? replacementLine : toPrint + replacementLine); + toPrint = null; + } + String lastReplacementLine = replacementSize > 0 ? replacement.get(replacementSize - 1) : ""; + toPrint = toPrint == null ? lastReplacementLine : toPrint + lastReplacementLine; + restOfLine = line.substring(index + pattern.length()); + } + } while (entryFound != null); + output.add(toPrint == null ? restOfLine : toPrint + restOfLine); + } + } + + public static void addReplacementToMap(Map<String, List<String>> map, String pattern, String replacement) { + List<String> replacementText = new ArrayList(); + replacementText.add(replacement); + map.put(pattern, replacementText); + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/org/thermostat/qa2/reporter/LogParser.java Thu Mar 19 12:44:59 2015 +0100 @@ -0,0 +1,142 @@ +/* + ThermostatQA - test framework for Thermostat Monitoring Tool + + Copyright 2015 Red Hat, Inc. + + This file is part of ThermostatQA + + ThermostatQA is distributed under the GNU General Public License, + version 2 or any later version (with a special exception described + below, commonly known as the "Classpath Exception"). + + A copy of GNU General Public License (GPL) is included in this + distribution, in the file COPYING. + + Linking ThermostatQA code with other modules is making a combined work + based on ThermostatQA. Thus, the terms and conditions of the GPL + cover the whole combination. + + As a special exception, the copyright holders of ThermostatQA give you + permission to link this code with independent modules to produce an + executable, regardless of the license terms of these independent + modules, and to copy and distribute the resulting executable under + terms of your choice, provided that you also meet, for each linked + independent module, the terms and conditions of the license of that + module. An independent module is a module which is not derived from + or based on ThermostatQA code. If you modify ThermostatQA, you may + extend this exception to your version of the software, but you are + not obligated to do so. If you do not wish to do so, delete this + exception statement from your version. + */ +package org.thermostat.qa2.reporter; + +import org.thermostat.qa2.reporter.result.TestMethodResult; +import org.thermostat.qa2.reporter.result.TestClassResult; +import org.thermostat.qa2.reporter.result.TestRunResult; +import java.io.File; +import java.io.IOException; +import java.text.Collator; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import static org.thermostat.qa2.framework.TestResult.*; +import org.thermostat.qa2.framework.TestResult; +import org.thermostat.qa2.framework.utils.FileUtilities; + +/** + * + * @author Zdeněk Žamberský + */ +public class LogParser { + + public static TestRunResult parseLogDir(File dir) throws IOException { + List<TestClassResult> testClasses = new ArrayList(); + for (File f : dir.listFiles()) { + testClasses.add(parseLog(f)); + } + Collections.sort(testClasses, new Comparator<TestClassResult>() { + Collator collator = Collator.getInstance(); + + @Override + public int compare(TestClassResult o1, TestClassResult o2) { + return collator.compare(o1.name, o2.name); + } + + }); + return new TestRunResult(dir.getName(), testClasses); + } + + public static TestClassResult parseLog(File file) throws IOException { + List<String> lineList = FileUtilities.getLineListFromFile(file.getAbsolutePath()); + List<TestMethodResult> methodList = new ArrayList(); + long duration = 0; + List<String> log = new ArrayList(); + for (int i = 0; i < lineList.size(); ++i) { + String line = lineList.get(i); + TestResult result = getResult(line); + if (result != null) { + String methodLine = line.substring(result.toString().length() + 1); + String methodName = getMethodName(methodLine); + TestMethodResult testMethod = new TestMethodResult(methodName, result); + if (result == FAILED) { + String reason = getReason(methodLine); + List<String> stackTrace = new ArrayList(); + for (;;) { + line = lineList.get(++i); + if (line.startsWith("INFO:") || line.startsWith("SUMMARY:")) { + --i; + break; + } + stackTrace.add(line); + } + for (String s : stackTrace) { + String pattern = "Caused by: java.lang.AssertionError:"; + if (s.startsWith(pattern)) { + testMethod.setReason(s.substring(pattern.length())); + } + } + testMethod.setStackTrace(stackTrace); + testMethod.setLog(log); + log = new ArrayList(); + } else { + log.clear(); + } + methodList.add(testMethod); + } else if (line.startsWith("INFO:")) { + log.add(line); + } else if (line.startsWith("SUMMARY")) { + int index = line.indexOf("duration:"); + if (index >= 0) { + String durationString = line.substring(line.indexOf(":", index) + 1).trim(); + duration = Long.parseLong(durationString); + } + } + } + String testName = file.getName(); + testName = testName.substring(0, testName.length() - 4); + TestClassResult test = new TestClassResult(testName, methodList, duration); + return test; + } + + public static TestResult getResult(String s) { + return s.startsWith(PASSED.toString()) ? PASSED + : s.startsWith(FAILED.toString()) ? FAILED + : s.startsWith(IGNORED.toString()) ? IGNORED + : s.startsWith(ERROR.toString()) ? ERROR + : null; + } + + public static String getMethodName(String line) { + int index1 = line.indexOf(':'); + String tmp = index1 >= 0 ? line.substring(0, index1) : line; + int index2 = tmp.lastIndexOf('.'); + return tmp.substring(index2 + 1).trim(); + } + + public static String getReason(String line) { + int index = line.indexOf(':'); + return index >= 0 ? line.substring(index + 1).trim() : ""; + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/org/thermostat/qa2/reporter/Reporter.java Thu Mar 19 12:44:59 2015 +0100 @@ -0,0 +1,112 @@ +/* + ThermostatQA - test framework for Thermostat Monitoring Tool + + Copyright 2015 Red Hat, Inc. + + This file is part of ThermostatQA + + ThermostatQA is distributed under the GNU General Public License, + version 2 or any later version (with a special exception described + below, commonly known as the "Classpath Exception"). + + A copy of GNU General Public License (GPL) is included in this + distribution, in the file COPYING. + + Linking ThermostatQA code with other modules is making a combined work + based on ThermostatQA. Thus, the terms and conditions of the GPL + cover the whole combination. + + As a special exception, the copyright holders of ThermostatQA give you + permission to link this code with independent modules to produce an + executable, regardless of the license terms of these independent + modules, and to copy and distribute the resulting executable under + terms of your choice, provided that you also meet, for each linked + independent module, the terms and conditions of the license of that + module. An independent module is a module which is not derived from + or based on ThermostatQA code. If you modify ThermostatQA, you may + extend this exception to your version of the software, but you are + not obligated to do so. If you do not wish to do so, delete this + exception statement from your version. + */ +package org.thermostat.qa2.reporter; + +import java.awt.Dimension; +import org.thermostat.qa2.reporter.result.TestRunResult; +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import org.thermostat.qa.reporter.ResultsGraphGenerator; +import org.thermostat.qa.reporter.TestResult; +import org.thermostat.qa2.framework.ThermostatQAConfig; +import org.thermostat.qa2.framework.utils.FileUtilities; + +/** + * + * @author Zdeněk Žamberský + */ +public class Reporter { + + public static void generateReport() throws IOException { + File logsDir = new File(ThermostatQAConfig.logsDir); + File[] runDirs = logsDir.listFiles(); + Arrays.sort(runDirs); + + int runsCount = runDirs.length; + int limit = 30; + if (runsCount > 0) { + List<TestRunResult> runs = new ArrayList(); + for (int i = Math.max(0, runsCount - limit); i < runsCount; ++i) { + TestRunResult run = LogParser.parseLogDir(runDirs[i]); + runs.add(run); + } + TestRunResult lastRun = runs.get(runs.size() - 1); + + // index page + String indexPagePath = ThermostatQAConfig.reportDir + File.separator + "index.xhtml"; + List<String> indexPageText = new ArrayList(); + Generator.generateIndexPage(indexPageText, lastRun); + FileUtilities.printLineListToFile(indexPagePath, indexPageText); + + // log page + String logPagePath = ThermostatQAConfig.reportDir + File.separator + "log_" + lastRun.date + ".xhtml"; + List<String> logPageText = new ArrayList(); + Generator.generateLogPage(logPageText, lastRun); + FileUtilities.printLineListToFile(logPagePath, logPageText); + + // history pages + String[] fileNames = {"hist_all_tests", "hist_failed_tests", "hist_10", "hist_20", "hist_30"}; + String[] labels = {"all tests", "failed tests", "last 10 results", "last 20 results", "last 30 results"}; + int[] counts = {30, 30, 10, 20, 30}; + boolean[] onlyFailed = {false, true, true, true, true}; + + for (int i = 0; i < fileNames.length; ++i) { + String historyPagePath = ThermostatQAConfig.reportDir + File.separator + fileNames[i] + ".xhtml"; + List<String> historyPageText = new ArrayList(); + Generator.generateHistoryPage(historyPageText, runs, labels[i], counts[i], onlyFailed[i]); + FileUtilities.printLineListToFile(historyPagePath, historyPageText); + } + + // graphs pages + fileNames = new String[]{"graph_all", "graph_10", "graph_20", "graph_30"}; + counts = new int[]{30, 10, 20, 30}; + for (int i = 0; i < fileNames.length; ++i) { + String graphsPagePath = ThermostatQAConfig.reportDir + File.separator + fileNames[i] + ".xhtml"; + List<String> graphPageText = new ArrayList(); + Generator.generateGraphPage(graphPageText, runs, counts[i]); + FileUtilities.printLineListToFile(graphsPagePath, graphPageText); + } + + // generate image using old reporter + Dimension graphSize = new Dimension(256, 256); + TestResult testResult = new TestResult(lastRun.passedCount, lastRun.failedCount, lastRun.errorCount, lastRun.ignoredCount); + (new ResultsGraphGenerator()).generateGraph(testResult, graphSize, ThermostatQAConfig.reportDir + File.separator + "graph.png"); + } + } + + public static void main(String[] args) throws IOException { + generateReport(); + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/org/thermostat/qa2/reporter/result/TestClassResult.java Thu Mar 19 12:44:59 2015 +0100 @@ -0,0 +1,85 @@ +/* + ThermostatQA - test framework for Thermostat Monitoring Tool + + Copyright 2015 Red Hat, Inc. + + This file is part of ThermostatQA + + ThermostatQA is distributed under the GNU General Public License, + version 2 or any later version (with a special exception described + below, commonly known as the "Classpath Exception"). + + A copy of GNU General Public License (GPL) is included in this + distribution, in the file COPYING. + + Linking ThermostatQA code with other modules is making a combined work + based on ThermostatQA. Thus, the terms and conditions of the GPL + cover the whole combination. + + As a special exception, the copyright holders of ThermostatQA give you + permission to link this code with independent modules to produce an + executable, regardless of the license terms of these independent + modules, and to copy and distribute the resulting executable under + terms of your choice, provided that you also meet, for each linked + independent module, the terms and conditions of the license of that + module. An independent module is a module which is not derived from + or based on ThermostatQA code. If you modify ThermostatQA, you may + extend this exception to your version of the software, but you are + not obligated to do so. If you do not wish to do so, delete this + exception statement from your version. + */ +package org.thermostat.qa2.reporter.result; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.thermostat.qa2.framework.TestResult; +import static org.thermostat.qa2.framework.TestResult.*; + +/** + * + * @author Zdeněk Žamberský + */ +public class TestClassResult { + + public String name; + public List<TestMethodResult> methods; + public Map<String, TestMethodResult> methodsMap = new HashMap(); + + public int methodCount; + public int passedCount; + public int failedCount; + public int errorCount; + public int ignoredCount; + + public long duration; + + public TestClassResult(String name, List<TestMethodResult> methods, long duration) { + this.name = name; + this.methods = methods; + this.duration = duration; + for (TestMethodResult method : methods) { + methodsMap.put(method.name, method); + TestResult result = method.getResult(); + switch (result) { + case PASSED: + ++passedCount; + break; + case FAILED: + ++failedCount; + break; + case ERROR: + ++errorCount; + break; + case IGNORED: + ++ignoredCount; + break; + } + ++methodCount; + } + } + + public TestMethodResult getMethod(String name) { + return methodsMap.get(name); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/org/thermostat/qa2/reporter/result/TestMethodResult.java Thu Mar 19 12:44:59 2015 +0100 @@ -0,0 +1,86 @@ +/* + ThermostatQA - test framework for Thermostat Monitoring Tool + + Copyright 2015 Red Hat, Inc. + + This file is part of ThermostatQA + + ThermostatQA is distributed under the GNU General Public License, + version 2 or any later version (with a special exception described + below, commonly known as the "Classpath Exception"). + + A copy of GNU General Public License (GPL) is included in this + distribution, in the file COPYING. + + Linking ThermostatQA code with other modules is making a combined work + based on ThermostatQA. Thus, the terms and conditions of the GPL + cover the whole combination. + + As a special exception, the copyright holders of ThermostatQA give you + permission to link this code with independent modules to produce an + executable, regardless of the license terms of these independent + modules, and to copy and distribute the resulting executable under + terms of your choice, provided that you also meet, for each linked + independent module, the terms and conditions of the license of that + module. An independent module is a module which is not derived from + or based on ThermostatQA code. If you modify ThermostatQA, you may + extend this exception to your version of the software, but you are + not obligated to do so. If you do not wish to do so, delete this + exception statement from your version. + */ +package org.thermostat.qa2.reporter.result; + +import java.util.List; +import org.thermostat.qa2.framework.TestResult; + +/** + * + * @author Zdeněk Žamberský + */ +public class TestMethodResult { + + public String name; + public TestResult result; + // + public String reason; + public List<String> stackTrace; + public List<String> log; + + public TestMethodResult(String name, TestResult result) { + this.name = name; + this.result = result; + } + + public String getName() { + return name; + } + + public TestResult getResult() { + return result; + } + + public String getReason() { + return reason; + } + + public List<String> getLog() { + return log; + } + + public List<String> getStackTrace() { + return stackTrace; + } + + public void setReason(String reason) { + this.reason = reason; + } + + public void setStackTrace(List<String> stackTrace) { + this.stackTrace = stackTrace; + } + + public void setLog(List<String> log) { + this.log = log; + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/org/thermostat/qa2/reporter/result/TestRunResult.java Thu Mar 19 12:44:59 2015 +0100 @@ -0,0 +1,68 @@ +/* + ThermostatQA - test framework for Thermostat Monitoring Tool + + Copyright 2015 Red Hat, Inc. + + This file is part of ThermostatQA + + ThermostatQA is distributed under the GNU General Public License, + version 2 or any later version (with a special exception described + below, commonly known as the "Classpath Exception"). + + A copy of GNU General Public License (GPL) is included in this + distribution, in the file COPYING. + + Linking ThermostatQA code with other modules is making a combined work + based on ThermostatQA. Thus, the terms and conditions of the GPL + cover the whole combination. + + As a special exception, the copyright holders of ThermostatQA give you + permission to link this code with independent modules to produce an + executable, regardless of the license terms of these independent + modules, and to copy and distribute the resulting executable under + terms of your choice, provided that you also meet, for each linked + independent module, the terms and conditions of the license of that + module. An independent module is a module which is not derived from + or based on ThermostatQA code. If you modify ThermostatQA, you may + extend this exception to your version of the software, but you are + not obligated to do so. If you do not wish to do so, delete this + exception statement from your version. + */ +package org.thermostat.qa2.reporter.result; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * + * @author Zdeněk Žamberský + */ +public class TestRunResult { + + public String date; + + public List<TestClassResult> results; + public Map<String, TestClassResult> resultsMap = new HashMap(); + + public int methodCount; + public int passedCount; + public int failedCount; + public int errorCount; + public int ignoredCount; + + public TestRunResult(String date, List<TestClassResult> results) { + this.date = date; + this.results = results; + for (TestClassResult result : results) { + methodCount += result.methodCount; + passedCount += result.passedCount; + failedCount += result.failedCount; + errorCount += result.errorCount; + ignoredCount += result.ignoredCount; + + resultsMap.put(result.name, result); + } + } + +}
--- a/templates/graph.html Thu Mar 19 12:25:06 2015 +0100 +++ b/templates/graph.html Thu Mar 19 12:44:59 2015 +0100 @@ -51,6 +51,7 @@ </head> <body> +<div> <h1>ThermostatQA tests report - graph for ${TEST_COUNT} tests</h1> <table border="0"> @@ -166,6 +167,7 @@ </tr> </table> <hr /> + </div> </body> </html>
--- a/templates/hist.html Thu Mar 19 12:25:06 2015 +0100 +++ b/templates/hist.html Thu Mar 19 12:44:59 2015 +0100 @@ -41,6 +41,7 @@ <link type="text/css" rel="StyleSheet" href="style.css" /> </head> <body> +<div> <h1>ThermostatQA tests history - ${RESULTS}</h1> <br /> @@ -48,6 +49,7 @@ <table border='2' frame='border' rules='all' cellspacing='1' cellpadding='1' class='forms' summary='' style='white-space:nowrap;margin-left:0px'> ${TABLE_DATA} </table> +</div> </body> </html>
--- a/templates/index.html Thu Mar 19 12:25:06 2015 +0100 +++ b/templates/index.html Thu Mar 19 12:44:59 2015 +0100 @@ -37,10 +37,11 @@ <title>ThermostatQA tests report: JDK ${JAVA_VERSION}</title> <meta name="Author" content="Pavel Tisnovsky" /> <meta name="Generator" content="org.thermostat.qa.reporter.Reporter" /> - <meta http-equiv="content-type" content="text/html; charset=utf-8" /> + <meta http-equiv="content-type" content="application/xhtml+xml; charset=utf-8" /> <link type="text/css" rel="StyleSheet" href="style.css" /> </head> <body> +<div> <h1>ThermostatQA tests report: JDK ${JAVA_VERSION}</h1> <p> @@ -48,16 +49,15 @@ <a href="${RESULTS_URL}"> ${RESULTS_URL}</a> </p> - - <br /> + <table border='2' frame='border' rules='all' cellspacing='1' cellpadding='1' class='forms' style='width:1000px' summary=''> <tr><td colspan='5' class='table-header'>Daily report</td></tr> <tr> <td rowspan='3' class='group-id'>${DATE}</td> <td>Log location:</td> - <td colspan='2'><a href='log_${DATE}.html'> - log_${DATE}.html</a></td> + <td colspan='2'><a href='log_${DATE}.xhtml'> + log_${DATE}.xhtml</a></td> </tr> <tr> <td style='width:20%' class='passed-header'>Passed</td> @@ -74,25 +74,25 @@ <tr> <td colspan='5'> </td> </tr> - <tr><td colspan='4' class='table-header'>Full history</td><td rowspan="18"><img src="graph.png" title="test results in graphical form"></td></tr> + <tr><td colspan='4' class='table-header'>Full history</td><td rowspan="18"><img src="graph.png" title="test results in graphical form" alt="missing image" /></td></tr> <tr> - <td> </td><td colspan='3'><a href='hist_all_tests.html'>All tests (huge page!)</a></td> + <td> </td><td colspan='3'><a href='hist_all_tests.xhtml'>All tests (huge page!)</a></td> </tr> <tr> - <td> </td><td colspan='3'><a href='hist_failed_tests.html'>Failed tests (more useful)</a></td> + <td> </td><td colspan='3'><a href='hist_failed_tests.xhtml'>Failed tests (more useful)</a></td> </tr> <tr> - <td> </td><td colspan='3'>Last <i>n</i> results: <a href='hist_10.html'>[10]</a><a href='hist_20.html'>[20]</a><a href='hist_30.html'>[30]</a></td> + <td> </td><td colspan='3'>Last <i>n</i> results: <a href='hist_10.xhtml'>[10]</a><a href='hist_20.xhtml'>[20]</a><a href='hist_30.xhtml'>[30]</a></td> </tr> <tr> <td colspan='4'> </td> </tr> <tr><td colspan='4' class='table-header'>Graphs</td></tr> <tr> - <td> </td><td colspan='3'><a href='graph_all.html'>All tests</a></td> + <td> </td><td colspan='3'><a href='graph_all.xhtml'>All tests</a></td> </tr> <tr> - <td> </td><td colspan='3'>Last <i>n</i> results: <a href='graph_10.html'>[10]</a><a href='graph_20.html'>[20]</a><a href='graph_30.html'>[30]</a></td> + <td> </td><td colspan='3'>Last <i>n</i> results: <a href='graph_10.xhtml'>[10]</a><a href='graph_20.xhtml'>[20]</a><a href='graph_30.xhtml'>[30]</a></td> </tr> <tr> <td colspan='4'> </td> @@ -127,6 +127,7 @@ <td colspan='3'>${VM_VERSION}</td> </tr> </table> +</div> </body> </html>
--- a/templates/log.html Thu Mar 19 12:25:06 2015 +0100 +++ b/templates/log.html Thu Mar 19 12:44:59 2015 +0100 @@ -41,6 +41,7 @@ <link type="text/css" rel="StyleSheet" href="style.css" /> </head> <body> +<div> <h1>ThermostatQA tests log for date ${DATE}</h1> <br /> @@ -59,6 +60,7 @@ <!-- it's needed to be able to display specific test on the top of browser window --> <div style='height:2000px;'> </div> +</div> </body> </html>
--- a/templates/style.css Thu Mar 19 12:25:06 2015 +0100 +++ b/templates/style.css Thu Mar 19 12:44:59 2015 +0100 @@ -65,8 +65,18 @@ .stack-trace {color:#604040} -.test-name-prefix {color:#808080} +.test-name-prefix {color:#606060} .test-name-postfix {color:#000000} .empty {background-color:#ffffff} +label { color:#0000ff; } +label:hover { text-decoration: underline; } +.log-toggle + label::after { content: "show details"; } +.log-toggle:checked + label::after { content: "hide details"; } +.log-toggle { display: none; } +.log-toggle ~ div { display: none; } +.log-toggle:checked ~ div { display: initial; } + +.reason {color:#303030; } +