| /******************************************************************************* |
| * Copyright (c) 2000, 2009 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.test.performance.ui; |
| |
| import java.io.BufferedOutputStream; |
| import java.io.File; |
| import java.io.FileNotFoundException; |
| import java.io.FileOutputStream; |
| import java.io.IOException; |
| import java.io.OutputStream; |
| import java.io.PrintStream; |
| import java.util.ArrayList; |
| import java.util.Iterator; |
| import java.util.List; |
| |
| import org.eclipse.core.runtime.OperationCanceledException; |
| import org.eclipse.core.runtime.SubMonitor; |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.graphics.Color; |
| import org.eclipse.swt.graphics.Image; |
| import org.eclipse.swt.graphics.ImageData; |
| import org.eclipse.swt.graphics.ImageLoader; |
| import org.eclipse.swt.widgets.Display; |
| import org.eclipse.test.internal.performance.data.Dim; |
| import org.eclipse.test.internal.performance.results.db.BuildResults; |
| import org.eclipse.test.internal.performance.results.db.ComponentResults; |
| import org.eclipse.test.internal.performance.results.db.ConfigResults; |
| import org.eclipse.test.internal.performance.results.db.DB_Results; |
| import org.eclipse.test.internal.performance.results.db.PerformanceResults; |
| import org.eclipse.test.internal.performance.results.db.ScenarioResults; |
| import org.eclipse.test.internal.performance.results.utils.Util; |
| |
| import junit.framework.AssertionFailedError; |
| |
| /** |
| * Class used to print scenario all builds data. |
| */ |
| public class ScenarioData { |
| private String baselinePrefix = null; |
| private List pointsOfInterest; |
| private List buildIDStreamPatterns; |
| private File rootDir; |
| private static final int GRAPH_WIDTH = 600; |
| private static final int GRAPH_HEIGHT = 200; |
| private Dim[] dimensions = DB_Results.getResultsDimensions(); |
| |
| /** |
| * Summary of results for a scenario for a given build compared to a |
| * reference. |
| * |
| * @param baselinePrefix The prefix of the baseline build names |
| * @param pointsOfInterest A list of buildId's to highlight on line graphs |
| * @param buildIDPatterns |
| * @param outputDir The directory root where the files are generated |
| * |
| */ |
| public ScenarioData(String baselinePrefix, List pointsOfInterest, List buildIDPatterns, File outputDir) { |
| this.baselinePrefix = baselinePrefix; |
| this.pointsOfInterest = pointsOfInterest; |
| this.buildIDStreamPatterns = buildIDPatterns; |
| this.rootDir = outputDir; |
| } |
| |
| /* |
| * Create a file handle verifying that its name does not go over |
| * the maximum authorized length. |
| */ |
| private File createFile(File outputDir, String subdir, String name, String extension) { |
| File dir = outputDir; |
| if (subdir != null) { |
| dir = new File(outputDir, subdir); |
| if (!dir.exists()) { |
| dir.mkdir(); |
| } |
| } |
| return new File(dir, name + '.' + extension); |
| } |
| |
| /* |
| * Returns a LineGraph object representing measurements for a scenario over builds. |
| */ |
| private TimeLineGraph getLineGraph(ScenarioResults scenarioResults, ConfigResults configResults, Dim dim, List highlightedPoints, List currentBuildIdPrefixes) { |
| Display display = Display.getDefault(); |
| |
| Color black = display.getSystemColor(SWT.COLOR_BLACK); |
| Color yellow = display.getSystemColor(SWT.COLOR_DARK_YELLOW); |
| Color magenta = display.getSystemColor(SWT.COLOR_MAGENTA); |
| |
| String scenarioName = scenarioResults.getName(); |
| TimeLineGraph graph = new TimeLineGraph(scenarioName + ": " + dim.getName(), dim); |
| String baseline = configResults.getBaselineBuildName(); |
| String current = configResults.getCurrentBuildName(); |
| |
| final String defaultBaselinePrefix = DB_Results.getDbBaselinePrefix(); |
| Iterator builds = configResults.getResults(); |
| List lastSevenNightlyBuilds = configResults.lastNightlyBuildNames(7); |
| buildLoop: while (builds.hasNext()) { |
| BuildResults buildResults = (BuildResults) builds.next(); |
| String buildID = buildResults.getName(); |
| int underscoreIndex = buildID.indexOf('-'); |
| String label = (underscoreIndex != -1 && buildID.equals(current)) ? buildID.substring(0, underscoreIndex) : buildID; |
| if (buildID.startsWith(defaultBaselinePrefix) && (underscoreIndex != -1)) { |
| label = defaultBaselinePrefix+buildID.charAt(defaultBaselinePrefix.length())+buildID.substring(underscoreIndex); |
| } |
| |
| double value = buildResults.getValue(dim.getId()); |
| |
| if (buildID.equals(current)) { |
| Color color = black; |
| if (buildID.startsWith("N")) |
| color = yellow; |
| |
| graph.addItem("main", label, dim.getDisplayValue(value), value, color, true, Utils.getDateFromBuildID(buildID), true); |
| continue; |
| } |
| if (highlightedPoints.contains(buildID)) { |
| graph.addItem("main", label, dim.getDisplayValue(value), value, black, false, Utils.getDateFromBuildID(buildID, false), true); |
| continue; |
| } |
| if (buildID.charAt(0) == 'N') { |
| if (lastSevenNightlyBuilds.contains(buildID)) { |
| graph.addItem("main", buildID, dim.getDisplayValue(value), value, yellow, false, Utils.getDateFromBuildID(buildID), false); |
| } |
| continue; |
| } |
| for (int i=0;i<currentBuildIdPrefixes.size();i++){ |
| if (buildID.startsWith(currentBuildIdPrefixes.get(i).toString())) { |
| graph.addItem("main", buildID, dim.getDisplayValue(value), value, black, false, Utils.getDateFromBuildID(buildID), false); |
| continue buildLoop; |
| } |
| } |
| if (buildID.equals(baseline)) { |
| boolean drawBaseline = (this.baselinePrefix != null) ? false : true; |
| graph.addItem("reference", label, dim.getDisplayValue(value), value, magenta, true, Utils.getDateFromBuildID(buildID, true), true, drawBaseline); |
| continue; |
| } |
| if (this.baselinePrefix != null) { |
| if (buildID.startsWith(this.baselinePrefix) && !buildID.equals(baseline) && Utils.getDateFromBuildID(buildID, true) <= Utils.getDateFromBuildID(baseline, true)) { |
| graph.addItem("reference", label, dim.getDisplayValue(value), value, magenta, false, Utils.getDateFromBuildID(buildID, true), false); |
| continue; |
| } |
| } |
| } |
| return graph; |
| } |
| |
| /** |
| * Print the scenario all builds data from the given performance results. |
| * |
| * @param performanceResults The needed information to generate scenario data |
| */ |
| public void print(PerformanceResults performanceResults, PrintStream printStream, final SubMonitor subMonitor) { |
| String[] configNames = performanceResults.getConfigNames(false/*not sorted*/); |
| String[] configBoxes = performanceResults.getConfigBoxes(false/*not sorted*/); |
| int length = configNames.length; |
| int size = performanceResults.size(); |
| double total = length * size; |
| subMonitor.setWorkRemaining(length*size); |
| int progress = 0; |
| for (int i=0; i<length; i++) { |
| final String configName = configNames[i]; |
| final String configBox = configBoxes[i]; |
| |
| // Manage monitor |
| // subMonitor.setTaskName("Generating data for "+configBox); |
| final String subTaskPrefix = "Generating data: "+configBox; |
| subMonitor.subTask(subTaskPrefix); |
| if (subMonitor.isCanceled()) throw new OperationCanceledException(); |
| |
| long start = System.currentTimeMillis(); |
| if (printStream != null) printStream.print(" + "+configName); |
| final File outputDir = new File(this.rootDir, configName); |
| outputDir.mkdir(); |
| Iterator components = performanceResults.getResults(); |
| while (components.hasNext()) { |
| if (printStream != null) printStream.print("."); |
| final ComponentResults componentResults = (ComponentResults) components.next(); |
| |
| // Manage monitor |
| int percentage = (int) ((progress++ / total) * 100); |
| // subMonitor.setTaskName("Generating data for "+configBox+": "+percentage+"%"); |
| // subMonitor.subTask("Component "+componentResults.getName()+"..."); |
| subMonitor.subTask(subTaskPrefix + " (" +componentResults.getName()+ ") "+ percentage + "%..."); |
| |
| Display display = Display.getDefault(); |
| display.syncExec( |
| new Runnable() { |
| public void run(){ |
| printSummary(configName, configBox, componentResults, outputDir, subMonitor); |
| } |
| } |
| ); |
| // printSummary(configName, configBox, componentResults, outputDir, monitor); |
| printDetails(configName, configBoxes[i], componentResults, outputDir); |
| |
| subMonitor.worked(1); |
| if (subMonitor.isCanceled()) throw new OperationCanceledException(); |
| } |
| if (printStream != null) { |
| String duration = Util.timeString(System.currentTimeMillis()-start); |
| printStream.println(" done in "+duration); |
| } |
| } |
| } |
| |
| /* |
| * Print the summary file of the builds data. |
| */ |
| void printSummary(String configName, String configBox, ComponentResults componentResults, File outputDir, SubMonitor subMonitor) { |
| Iterator scenarios = componentResults.getResults(); |
| while (scenarios.hasNext()) { |
| List highlightedPoints = new ArrayList(); |
| ScenarioResults scenarioResults = (ScenarioResults) scenarios.next(); |
| ConfigResults configResults = scenarioResults.getConfigResults(configName); |
| if (configResults == null || !configResults.isValid()) continue; |
| |
| // get latest points of interest matching |
| if (this.pointsOfInterest != null) { |
| Iterator buildPrefixes = this.pointsOfInterest.iterator(); |
| while (buildPrefixes.hasNext()) { |
| String buildPrefix = (String) buildPrefixes.next(); |
| List builds = configResults.getBuilds(buildPrefix); |
| if (buildPrefix.indexOf('*') <0 && buildPrefix.indexOf('?') < 0) { |
| if (builds.size() > 0) { |
| highlightedPoints.add(builds.get(builds.size()-1)); |
| } |
| } else { |
| highlightedPoints.addAll(builds); |
| } |
| } |
| } |
| |
| String scenarioFileName = scenarioResults.getFileName(); |
| File outputFile = new File(outputDir, scenarioFileName+".html"); |
| PrintStream stream = null; |
| try { |
| stream = new PrintStream(new BufferedOutputStream(new FileOutputStream(outputFile))); |
| } catch (FileNotFoundException e) { |
| System.err.println("can't create output file" + outputFile); //$NON-NLS-1$ |
| } |
| if (stream == null) { |
| stream = System.out; |
| } |
| stream.print(Utils.HTML_OPEN); |
| stream.print(Utils.HTML_DEFAULT_CSS); |
| |
| stream.print("<title>" + scenarioResults.getName() + "(" + configBox + ")" + "</title></head>\n"); //$NON-NLS-1$ |
| stream.print("<h4>Scenario: " + scenarioResults.getName() + " (" + configBox + ")</h4><br>\n"); //$NON-NLS-1$ //$NON-NLS-2$ |
| |
| String failureMessage = Utils.failureMessage(configResults.getCurrentBuildDeltaInfo(), true); |
| if (failureMessage != null){ |
| stream.print("<table><tr><td><b>"+failureMessage+"</td></tr></table>\n"); |
| } |
| |
| BuildResults currentBuildResults = configResults.getCurrentBuildResults(); |
| String comment = currentBuildResults.getComment(); |
| if (comment != null) { |
| stream.print("<p><b>Note:</b><br>\n"); |
| stream.print(comment + "</p>\n"); |
| } |
| |
| // Print link to raw data. |
| String rawDataFile = "raw/" + scenarioFileName+".html"; |
| stream.print("<br><br><b><a href=\""+rawDataFile+"\">Raw data and Stats</a></b><br><br>\n"); |
| stream.print("<b>Click measurement name to view line graph of measured values over builds.</b><br><br>\n"); |
| if (subMonitor.isCanceled()) throw new OperationCanceledException(); |
| |
| try { |
| // Print build result table |
| stream.print("<table border=\"1\">\n"); //$NON-NLS-1$ |
| stream.print("<tr><td><b>Build Id</b></td>"); //$NON-NLS-1$ |
| int dimLength = this.dimensions.length; |
| for (int d=0; d<dimLength; d++) { |
| Dim dim = this.dimensions[d]; |
| stream.print("<td><a href=\"#" + dim.getLabel() + "\"><b>" + dim.getName() + "</b></a></td>"); |
| } |
| stream.print("</tr>\n"); |
| |
| // Write build lines |
| printTableLine(stream, currentBuildResults); |
| printTableLine(stream, configResults.getBaselineBuildResults()); |
| |
| // Write difference line |
| printDifferenceLine(stream, configResults); |
| |
| // End of table |
| stream.print("</table>\n"); |
| stream.print("*Delta values in red and green indicate degradation > 10% and improvement > 10%,respectively.<br><br>\n"); |
| stream.print("<br><hr>\n\n"); |
| |
| // print text legend. |
| stream.print("Black and yellow points plot values measured in integration and last seven nightly builds.<br>\n" + "Magenta points plot the repeated baseline measurement over time.<br>\n" |
| + "Boxed points represent previous releases, milestone builds, current reference and current build.<br><br>\n" |
| + "Hover over any point for build id and value.\n"); |
| |
| // print image maps of historical |
| for (int d=0; d<dimLength; d++) { |
| Dim dim = this.dimensions[d]; |
| TimeLineGraph lineGraph = getLineGraph(scenarioResults, configResults, dim, highlightedPoints, this.buildIDStreamPatterns); |
| if (subMonitor.isCanceled()) throw new OperationCanceledException(); |
| |
| String dimShortName = dim.getLabel(); |
| String imgFileName = scenarioFileName + "_" + dimShortName; |
| File imgFile = createFile(outputDir, "graphs", imgFileName, "gif"); |
| saveGraph(lineGraph, imgFile); |
| stream.print("<br><a name=\"" + dimShortName + "\"></a>\n"); |
| stream.print("<br><b>" + dim.getName() + "</b><br>\n"); |
| stream.print(dim.getDescription() + "<br><br>\n"); |
| stream.print("<img src=\"graphs/"); |
| stream.print(imgFile.getName()); |
| stream.print("\" usemap=\"#" + lineGraph.fTitle + "\">"); |
| stream.print("<map name=\"" + lineGraph.fTitle + "\">"); |
| stream.print(lineGraph.getAreas()); |
| stream.print("</map>\n"); |
| if (subMonitor.isCanceled()) throw new OperationCanceledException(); |
| } |
| stream.print("<br><br></body>\n"); |
| stream.print(Utils.HTML_CLOSE); |
| if (stream != System.out) |
| stream.close(); |
| |
| } catch (AssertionFailedError e) { |
| e.printStackTrace(); |
| continue; |
| } |
| } |
| } |
| |
| /* |
| * Print the data for a build results. |
| */ |
| private void printTableLine(PrintStream stream, BuildResults buildResults) { |
| stream.print("<tr><td>"); |
| stream.print(buildResults.getName()); |
| if (buildResults.isBaseline()) stream.print(" (reference)"); |
| stream.print("</td>"); |
| int dimLength = this.dimensions.length; |
| for (int d=0; d<dimLength; d++) { |
| Dim dim = this.dimensions[d]; |
| int dim_id = dim.getId(); |
| double stddev = buildResults.getDeviation(dim_id); |
| String displayValue = dim.getDisplayValue(buildResults.getValue(dim_id)); |
| stream.print("<td>"); |
| stream.print(displayValue); |
| if (stddev < 0) { |
| stream.print(" [n/a]\n"); |
| } else if (stddev > 0) { |
| stream.print(" ["); |
| stream.print(dim.getDisplayValue(stddev)); |
| stream.print("]"); |
| } |
| stream.print( "</td>"); |
| } |
| stream.print("</tr>\n"); |
| } |
| |
| /* |
| * Print the line showing the difference between current and baseline builds. |
| */ |
| private void printDifferenceLine(PrintStream stream, ConfigResults configResults) { |
| stream.print("<tr><td>*Delta</td>"); |
| int dimLength = this.dimensions.length; |
| for (int d=0; d<dimLength; d++) { |
| Dim currentDim = this.dimensions[d]; |
| int dim_id = currentDim.getId(); |
| BuildResults currentBuild = configResults.getCurrentBuildResults(); |
| BuildResults baselineBuild = configResults.getBaselineBuildResults(); |
| |
| // Compute difference values |
| double baselineValue = baselineBuild.getValue(dim_id); |
| double diffValue = baselineValue - currentBuild.getValue(dim_id); |
| double diffPercentage = baselineValue == 0 ? 0 : Math.round(diffValue / baselineValue * 1000) / 10.0; |
| String diffDisplayValue = currentDim.getDisplayValue(diffValue); |
| |
| // Set colors |
| String fontColor = ""; |
| if (diffPercentage > 10) { |
| fontColor = "#006600"; // green |
| } |
| if (diffPercentage < -10) { |
| fontColor = "#FF0000"; // red |
| } |
| |
| // Print line |
| String percentage = (diffPercentage == 0) ? "" : "<br>" + diffPercentage + " %"; |
| if (diffPercentage > 10 || diffPercentage < -10) { |
| stream.print("<td><FONT COLOR=\"" + fontColor + "\"><b>" + diffDisplayValue + percentage + "</b></FONT></td>"); |
| } else { |
| stream.print("<td>" + diffDisplayValue + percentage + "</td>"); |
| } |
| } |
| stream.print("</tr></font>"); |
| } |
| |
| /* |
| * Print details file of the scenario builds data. |
| */ |
| private void printDetails(String configName, String configBox, ComponentResults componentResults, File outputDir) { |
| Iterator scenarios = componentResults.getResults(); |
| while (scenarios.hasNext()) { |
| ScenarioResults scenarioResults = (ScenarioResults) scenarios.next(); |
| ConfigResults configResults = scenarioResults.getConfigResults(configName); |
| if (configResults == null || !configResults.isValid()) continue; |
| String scenarioName= scenarioResults.getName(); |
| String scenarioFileName = scenarioResults.getFileName(); |
| File outputFile = createFile(outputDir, "raw", scenarioFileName, "html"); |
| PrintStream stream = null; |
| try { |
| stream = new PrintStream(new BufferedOutputStream(new FileOutputStream(outputFile))); |
| } catch (FileNotFoundException e) { |
| System.err.println("can't create output file" + outputFile); //$NON-NLS-1$ |
| } |
| if (stream == null) stream = System.out; |
| RawDataTable currentResultsTable = new RawDataTable(configResults, this.buildIDStreamPatterns, stream); |
| RawDataTable baselineResultsTable = new RawDataTable(configResults, this.baselinePrefix, stream); |
| stream.print(Utils.HTML_OPEN); |
| stream.print(Utils.HTML_DEFAULT_CSS); |
| stream.print("<title>" + scenarioName + "(" + configBox + ")" + " - Details</title></head>\n"); //$NON-NLS-1$ |
| stream.print("<h4>Scenario: " + scenarioName + " (" + configBox + ")</h4>\n"); //$NON-NLS-1$ |
| stream.print("<a href=\"../"+scenarioFileName+".html\">VIEW GRAPH</a><br><br>\n"); //$NON-NLS-1$ |
| stream.print("<table><td><b>Current Stream Test Runs</b></td><td><b>Baseline Test Runs</b></td></tr>\n"); |
| stream.print("<tr valign=\"top\">\n"); |
| stream.print("<td>"); |
| currentResultsTable.print(); |
| stream.print("</td>\n"); |
| stream.print("<td>"); |
| baselineResultsTable.print(); |
| stream.print("</td>\n"); |
| stream.print("</tr>\n"); |
| stream.print("</table>\n"); |
| stream.close(); |
| } |
| } |
| |
| /* |
| * Prints a LineGraph object as a gif file. |
| */ |
| private void saveGraph(LineGraph p, File outputFile) { |
| Image image = new Image(Display.getDefault(), GRAPH_WIDTH, GRAPH_HEIGHT); |
| p.paint(image); |
| |
| /* Downscale to 8 bit depth palette to save to gif */ |
| ImageData data = Utils.downSample(image); |
| ImageLoader il = new ImageLoader(); |
| il.data = new ImageData[] { data }; |
| OutputStream out = null; |
| try { |
| out = new BufferedOutputStream(new FileOutputStream(outputFile)); |
| il.save(out, SWT.IMAGE_GIF); |
| |
| } catch (FileNotFoundException e) { |
| e.printStackTrace(); |
| } finally { |
| image.dispose(); |
| if (out != null) { |
| try { |
| out.close(); |
| } catch (IOException e1) { |
| // silently ignored |
| } |
| } |
| } |
| } |
| } |