Home | History | Annotate | Download | only in ui
      1 /*******************************************************************************
      2  * Copyright (c) 2000, 2009 IBM Corporation and others.
      3  * All rights reserved. This program and the accompanying materials
      4  * are made available under the terms of the Eclipse Public License v1.0
      5  * which accompanies this distribution, and is available at
      6  * http://www.eclipse.org/legal/epl-v10.html
      7  *
      8  * Contributors:
      9  *     IBM Corporation - initial API and implementation
     10  *******************************************************************************/
     11 package org.eclipse.test.performance.ui;
     12 
     13 import java.io.BufferedOutputStream;
     14 import java.io.File;
     15 import java.io.FileNotFoundException;
     16 import java.io.FileOutputStream;
     17 import java.io.IOException;
     18 import java.io.OutputStream;
     19 import java.io.PrintStream;
     20 import java.util.ArrayList;
     21 import java.util.Iterator;
     22 import java.util.List;
     23 
     24 import junit.framework.AssertionFailedError;
     25 
     26 import org.eclipse.core.runtime.OperationCanceledException;
     27 import org.eclipse.core.runtime.SubMonitor;
     28 import org.eclipse.swt.SWT;
     29 import org.eclipse.swt.graphics.Color;
     30 import org.eclipse.swt.graphics.Image;
     31 import org.eclipse.swt.graphics.ImageData;
     32 import org.eclipse.swt.graphics.ImageLoader;
     33 import org.eclipse.swt.widgets.Display;
     34 import org.eclipse.test.internal.performance.data.Dim;
     35 import org.eclipse.test.internal.performance.results.db.BuildResults;
     36 import org.eclipse.test.internal.performance.results.db.ComponentResults;
     37 import org.eclipse.test.internal.performance.results.db.ConfigResults;
     38 import org.eclipse.test.internal.performance.results.db.DB_Results;
     39 import org.eclipse.test.internal.performance.results.db.PerformanceResults;
     40 import org.eclipse.test.internal.performance.results.db.ScenarioResults;
     41 import org.eclipse.test.internal.performance.results.utils.Util;
     42 
     43 /**
     44  * Class used to print scenario all builds data.
     45  */
     46 public class ScenarioData {
     47 	private String baselinePrefix = null;
     48 	private List pointsOfInterest;
     49 	private List buildIDStreamPatterns;
     50 	private File rootDir;
     51 	private static final int GRAPH_WIDTH = 600;
     52 	private static final int GRAPH_HEIGHT = 200;
     53 	private Dim[] dimensions = DB_Results.getResultsDimensions();
     54 
     55 /**
     56  * Summary of results for a scenario for a given build compared to a
     57  * reference.
     58  *
     59  * @param baselinePrefix The prefix of the baseline build names
     60  * @param pointsOfInterest A list of buildId's to highlight on line graphs
     61  * @param buildIDPatterns
     62  * @param outputDir The directory root where the files are generated
     63  *
     64 */
     65 public ScenarioData(String baselinePrefix, List pointsOfInterest, List buildIDPatterns, File outputDir) {
     66 	this.baselinePrefix = baselinePrefix;
     67 	this.pointsOfInterest = pointsOfInterest;
     68 	this.buildIDStreamPatterns = buildIDPatterns;
     69 	this.rootDir = outputDir;
     70 }
     71 
     72 /*
     73  * Create a file handle verifying that its name does not go over
     74  * the maximum authorized length.
     75  */
     76 private File createFile(File outputDir, String subdir, String name, String extension) {
     77 	File dir = outputDir;
     78 	if (subdir != null) {
     79 		dir = new File(outputDir, subdir);
     80 		if (!dir.exists()) {
     81 			dir.mkdir();
     82 		}
     83 	}
     84 	return new File(dir, name + '.' + extension);
     85 }
     86 
     87 /*
     88  * Returns a LineGraph object representing measurements for a scenario over builds.
     89  */
     90 private TimeLineGraph getLineGraph(ScenarioResults scenarioResults, ConfigResults configResults, Dim dim, List highlightedPoints, List currentBuildIdPrefixes) {
     91 	Display display = Display.getDefault();
     92 
     93 	Color black = display.getSystemColor(SWT.COLOR_BLACK);
     94 	Color yellow = display.getSystemColor(SWT.COLOR_DARK_YELLOW);
     95 	Color magenta = display.getSystemColor(SWT.COLOR_MAGENTA);
     96 
     97 	String scenarioName = scenarioResults.getName();
     98 	TimeLineGraph graph = new TimeLineGraph(scenarioName + ": " + dim.getName(), dim);
     99 	String baseline = configResults.getBaselineBuildName();
    100 	String current = configResults.getCurrentBuildName();
    101 
    102 	final String defaultBaselinePrefix = DB_Results.getDbBaselinePrefix();
    103 	Iterator builds = configResults.getResults();
    104 	List lastSevenNightlyBuilds = configResults.lastNightlyBuildNames(7);
    105 	buildLoop: while (builds.hasNext()) {
    106 		BuildResults buildResults = (BuildResults) builds.next();
    107 		String buildID = buildResults.getName();
    108 		int underscoreIndex = buildID.indexOf('_');
    109 		String label = (underscoreIndex != -1 && buildID.equals(current)) ? buildID.substring(0, underscoreIndex) : buildID;
    110 		if (buildID.startsWith(defaultBaselinePrefix)) {
    111 			label = defaultBaselinePrefix+buildID.charAt(defaultBaselinePrefix.length())+buildID.substring(underscoreIndex);
    112 		}
    113 
    114 		double value = buildResults.getValue(dim.getId());
    115 
    116 		if (buildID.equals(current)) {
    117 			Color color = black;
    118 			if (buildID.startsWith("N"))
    119 				color = yellow;
    120 
    121 			graph.addItem("main", label, dim.getDisplayValue(value), value, color, true, Utils.getDateFromBuildID(buildID), true);
    122 			continue;
    123 		}
    124 		if (highlightedPoints.contains(buildID)) {
    125 			graph.addItem("main", label, dim.getDisplayValue(value), value, black, false, Utils.getDateFromBuildID(buildID, false), true);
    126 			continue;
    127 		}
    128 		if (buildID.charAt(0) == 'N') {
    129 			if (lastSevenNightlyBuilds.contains(buildID)) {
    130 				graph.addItem("main", buildID, dim.getDisplayValue(value), value, yellow, false, Utils.getDateFromBuildID(buildID), false);
    131 			}
    132 			continue;
    133 		}
    134 		for (int i=0;i<currentBuildIdPrefixes.size();i++){
    135 			if (buildID.startsWith(currentBuildIdPrefixes.get(i).toString())) {
    136 				graph.addItem("main", buildID, dim.getDisplayValue(value), value, black, false, Utils.getDateFromBuildID(buildID), false);
    137 				continue buildLoop;
    138 			}
    139 		}
    140 		if (buildID.equals(baseline)) {
    141 			boolean drawBaseline = (this.baselinePrefix != null) ? false : true;
    142 			graph.addItem("reference", label, dim.getDisplayValue(value), value, magenta, true, Utils.getDateFromBuildID(buildID, true), true, drawBaseline);
    143 			continue;
    144 		}
    145 		if (this.baselinePrefix != null) {
    146 			if (buildID.startsWith(this.baselinePrefix) && !buildID.equals(baseline) && Utils.getDateFromBuildID(buildID, true) <= Utils.getDateFromBuildID(baseline, true)) {
    147 				graph.addItem("reference", label, dim.getDisplayValue(value), value, magenta, false, Utils.getDateFromBuildID(buildID, true), false);
    148 				continue;
    149 			}
    150 		}
    151 	}
    152 	return graph;
    153 }
    154 
    155 /**
    156  * Print the scenario all builds data from the given performance results.
    157  *
    158  * @param performanceResults The needed information to generate scenario data
    159  */
    160 public void print(PerformanceResults performanceResults, PrintStream printStream, final SubMonitor subMonitor) {
    161 	String[] configNames = performanceResults.getConfigNames(false/*not sorted*/);
    162 	String[] configBoxes = performanceResults.getConfigBoxes(false/*not sorted*/);
    163 	int length = configNames.length;
    164 	int size = performanceResults.size();
    165 	double total = length * size;
    166 	subMonitor.setWorkRemaining(length*size);
    167 	int progress = 0;
    168 	for (int i=0; i<length; i++) {
    169 		final String configName = configNames[i];
    170 		final String configBox = configBoxes[i];
    171 
    172 		// Manage monitor
    173 		subMonitor.setTaskName("Generating data for "+configBox);
    174 		if (subMonitor.isCanceled()) throw new OperationCanceledException();
    175 
    176 		long start = System.currentTimeMillis();
    177 		if (printStream != null) printStream.print("		+ "+configName);
    178 		final File outputDir = new File(this.rootDir, configName);
    179 		outputDir.mkdir();
    180 		Iterator components = performanceResults.getResults();
    181 		while (components.hasNext()) {
    182 			if (printStream != null) printStream.print(".");
    183 			final ComponentResults componentResults = (ComponentResults) components.next();
    184 
    185 			// Manage monitor
    186 			int percentage = (int) ((progress++ / total) * 100);
    187 			subMonitor.setTaskName("Generating data for "+configBox+": "+percentage+"%");
    188 			subMonitor.subTask("Component "+componentResults.getName()+"...");
    189 
    190 			Display display = Display.getDefault();
    191 		     display.syncExec(
    192 				new Runnable() {
    193 					public void run(){
    194 						printSummary(configName, configBox, componentResults, outputDir, subMonitor);
    195 					}
    196 				}
    197 			);
    198 //			printSummary(configName, configBox, componentResults, outputDir, monitor);
    199 			printDetails(configName, configBoxes[i], componentResults, outputDir);
    200 
    201 			subMonitor.worked(1);
    202 			if (subMonitor.isCanceled()) throw new OperationCanceledException();
    203 		}
    204 		if (printStream != null) {
    205 			String duration = Util.timeString(System.currentTimeMillis()-start);
    206 			printStream.println(" done in "+duration);
    207 		}
    208 	}
    209 }
    210 
    211 /*
    212  * Print the summary file of the builds data.
    213  */
    214 void printSummary(String configName, String configBox, ComponentResults componentResults, File outputDir, SubMonitor subMonitor) {
    215 	Iterator scenarios = componentResults.getResults();
    216 	while (scenarios.hasNext()) {
    217 		List highlightedPoints = new ArrayList();
    218 		ScenarioResults scenarioResults = (ScenarioResults) scenarios.next();
    219 		ConfigResults configResults = scenarioResults.getConfigResults(configName);
    220 		if (configResults == null || !configResults.isValid()) continue;
    221 
    222 		// get latest points of interest matching
    223 		if (this.pointsOfInterest != null) {
    224 			Iterator buildPrefixes = this.pointsOfInterest.iterator();
    225 			while (buildPrefixes.hasNext()) {
    226 				String buildPrefix = (String) buildPrefixes.next();
    227 				List builds = configResults.getBuilds(buildPrefix);
    228 				if (buildPrefix.indexOf('*') <0 && buildPrefix.indexOf('?') < 0) {
    229 					if (builds.size() > 0) {
    230 						highlightedPoints.add(builds.get(builds.size()-1));
    231 					}
    232 				} else {
    233 					highlightedPoints.addAll(builds);
    234 				}
    235 			}
    236 		}
    237 
    238 		String scenarioFileName = scenarioResults.getFileName();
    239 		File outputFile = new File(outputDir, scenarioFileName+".html");
    240 		PrintStream stream = null;
    241 		try {
    242 			stream = new PrintStream(new BufferedOutputStream(new FileOutputStream(outputFile)));
    243 		} catch (FileNotFoundException e) {
    244 			System.err.println("can't create output file" + outputFile); //$NON-NLS-1$
    245 		}
    246 		if (stream == null) {
    247 			stream = System.out;
    248 		}
    249 		stream.print(Utils.HTML_OPEN);
    250 		stream.print(Utils.HTML_DEFAULT_CSS);
    251 
    252 		stream.print("<title>" + scenarioResults.getName() + "(" + configBox + ")" + "</title></head>\n"); //$NON-NLS-1$
    253 		stream.print("<h4>Scenario: " + scenarioResults.getName() + " (" + configBox + ")</h4><br>\n"); //$NON-NLS-1$ //$NON-NLS-2$
    254 
    255 		String failureMessage = Utils.failureMessage(configResults.getCurrentBuildDeltaInfo(), true);
    256  		if (failureMessage != null){
    257    			stream.print("<table><tr><td><b>"+failureMessage+"</td></tr></table>\n");
    258  		}
    259 
    260  		BuildResults currentBuildResults = configResults.getCurrentBuildResults();
    261  		String comment = currentBuildResults.getComment();
    262 		if (comment != null) {
    263 			stream.print("<p><b>Note:</b><br>\n");
    264 			stream.print(comment + "</p>\n");
    265 		}
    266 
    267 		// Print link to raw data.
    268 		String rawDataFile = "raw/" + scenarioFileName+".html";
    269 		stream.print("<br><br><b><a href=\""+rawDataFile+"\">Raw data and Stats</a></b><br><br>\n");
    270 		stream.print("<b>Click measurement name to view line graph of measured values over builds.</b><br><br>\n");
    271 		if (subMonitor.isCanceled()) throw new OperationCanceledException();
    272 
    273 		try {
    274 			// Print build result table
    275 			stream.print("<table border=\"1\">\n"); //$NON-NLS-1$
    276 			stream.print("<tr><td><b>Build Id</b></td>"); //$NON-NLS-1$
    277 			int dimLength = this.dimensions.length;
    278 			for (int d=0; d<dimLength; d++) {
    279 				Dim dim = this.dimensions[d];
    280 				stream.print("<td><a href=\"#" + dim.getLabel() + "\"><b>" + dim.getName() + "</b></a></td>");
    281 			}
    282 			stream.print("</tr>\n");
    283 
    284 			// Write build lines
    285 			printTableLine(stream, currentBuildResults);
    286 			printTableLine(stream, configResults.getBaselineBuildResults());
    287 
    288 			// Write difference line
    289 			printDifferenceLine(stream, configResults);
    290 
    291 			// End of table
    292 			stream.print("</table>\n");
    293 			stream.print("*Delta values in red and green indicate degradation > 10% and improvement > 10%,respectively.<br><br>\n");
    294 			stream.print("<br><hr>\n\n");
    295 
    296 			// print text legend.
    297 			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"
    298 					+ "Boxed points represent previous releases, milestone builds, current reference and current build.<br><br>\n"
    299 					+ "Hover over any point for build id and value.\n");
    300 
    301 			// print image maps of historical
    302 			for (int d=0; d<dimLength; d++) {
    303 				Dim dim = this.dimensions[d];
    304 				TimeLineGraph lineGraph = getLineGraph(scenarioResults, configResults, dim, highlightedPoints, this.buildIDStreamPatterns);
    305 				if (subMonitor.isCanceled()) throw new OperationCanceledException();
    306 
    307 				String dimShortName = dim.getLabel();
    308 				String imgFileName = scenarioFileName + "_" + dimShortName;
    309 				File imgFile = createFile(outputDir, "graphs", imgFileName, "gif");
    310 				saveGraph(lineGraph, imgFile);
    311 				stream.print("<br><a name=\"" + dimShortName + "\"></a>\n");
    312 				stream.print("<br><b>" + dim.getName() + "</b><br>\n");
    313 				stream.print(dim.getDescription() + "<br><br>\n");
    314 				stream.print("<img src=\"graphs/");
    315 				stream.print(imgFile.getName());
    316 				stream.print("\" usemap=\"#" + lineGraph.fTitle + "\">");
    317 				stream.print("<map name=\"" + lineGraph.fTitle + "\">");
    318 				stream.print(lineGraph.getAreas());
    319 				stream.print("</map>\n");
    320 				if (subMonitor.isCanceled()) throw new OperationCanceledException();
    321 			}
    322 			stream.print("<br><br></body>\n");
    323 			stream.print(Utils.HTML_CLOSE);
    324 			if (stream != System.out)
    325 				stream.close();
    326 
    327 		} catch (AssertionFailedError e) {
    328 			e.printStackTrace();
    329 			continue;
    330 		}
    331 	}
    332 }
    333 
    334 /*
    335  * Print the data for a build results.
    336  */
    337 private void printTableLine(PrintStream stream, BuildResults buildResults) {
    338 	stream.print("<tr><td>");
    339 	stream.print(buildResults.getName());
    340 	if (buildResults.isBaseline()) stream.print(" (reference)");
    341 	stream.print("</td>");
    342 	int dimLength = this.dimensions.length;
    343 	for (int d=0; d<dimLength; d++) {
    344 		Dim dim = this.dimensions[d];
    345 		int dim_id = dim.getId();
    346 		double stddev = buildResults.getDeviation(dim_id);
    347 		String displayValue = dim.getDisplayValue(buildResults.getValue(dim_id));
    348 		stream.print("<td>");
    349 		stream.print(displayValue);
    350 		if (stddev < 0) {
    351 			stream.print(" [n/a]\n");
    352 		} else if (stddev > 0) {
    353 			stream.print(" [");
    354 			stream.print(dim.getDisplayValue(stddev));
    355 			stream.print("]");
    356 		}
    357 		stream.print( "</td>");
    358 	}
    359 	stream.print("</tr>\n");
    360 }
    361 
    362 /*
    363  * Print the line showing the difference between current and baseline builds.
    364  */
    365 private void printDifferenceLine(PrintStream stream, ConfigResults configResults) {
    366 	stream.print("<tr><td>*Delta</td>");
    367 	int dimLength = this.dimensions.length;
    368 	for (int d=0; d<dimLength; d++) {
    369 		Dim currentDim = this.dimensions[d];
    370 		int dim_id = currentDim.getId();
    371 		BuildResults currentBuild = configResults.getCurrentBuildResults();
    372 		BuildResults baselineBuild = configResults.getBaselineBuildResults();
    373 
    374 		// Compute difference values
    375 		double baselineValue = baselineBuild.getValue(dim_id);
    376 		double diffValue = baselineValue - currentBuild.getValue(dim_id);
    377 		double diffPercentage =  baselineValue == 0 ? 0 : Math.round(diffValue / baselineValue * 1000) / 10.0;
    378 		String diffDisplayValue = currentDim.getDisplayValue(diffValue);
    379 
    380 		// Set colors
    381 		String fontColor = "";
    382 		if (diffPercentage > 10) {
    383 			fontColor = "#006600";	// green
    384 		}
    385 		if (diffPercentage < -10) {
    386 			fontColor = "#FF0000";	// red
    387 		}
    388 
    389 		// Print line
    390 		String percentage = (diffPercentage == 0) ? "" : "<br>" + diffPercentage + " %";
    391 		if (diffPercentage > 10 || diffPercentage < -10) {
    392 			stream.print("<td><FONT COLOR=\"" + fontColor + "\"><b>" + diffDisplayValue + percentage + "</b></FONT></td>");
    393 		} else {
    394 			stream.print("<td>" + diffDisplayValue + percentage + "</td>");
    395 		}
    396 	}
    397 	stream.print("</tr></font>");
    398 }
    399 
    400 /*
    401  * Print details file of the scenario builds data.
    402  */
    403 private void printDetails(String configName, String configBox, ComponentResults componentResults, File outputDir) {
    404 	Iterator scenarios = componentResults.getResults();
    405 	while (scenarios.hasNext()) {
    406 		ScenarioResults scenarioResults = (ScenarioResults) scenarios.next();
    407 		ConfigResults configResults = scenarioResults.getConfigResults(configName);
    408 		if (configResults == null || !configResults.isValid()) continue;
    409 		String scenarioName= scenarioResults.getName();
    410 		String scenarioFileName = scenarioResults.getFileName();
    411 		File outputFile = createFile(outputDir, "raw", scenarioFileName, "html");
    412 		PrintStream stream = null;
    413 		try {
    414 			stream = new PrintStream(new BufferedOutputStream(new FileOutputStream(outputFile)));
    415 		} catch (FileNotFoundException e) {
    416 			System.err.println("can't create output file" + outputFile); //$NON-NLS-1$
    417 		}
    418 		if (stream == null) stream = System.out;
    419 		RawDataTable currentResultsTable = new RawDataTable(configResults, this.buildIDStreamPatterns, stream);
    420 		RawDataTable baselineResultsTable = new RawDataTable(configResults, this.baselinePrefix, stream);
    421 		stream.print(Utils.HTML_OPEN);
    422 		stream.print(Utils.HTML_DEFAULT_CSS);
    423 		stream.print("<title>" + scenarioName + "(" + configBox + ")" + " - Details</title></head>\n"); //$NON-NLS-1$
    424 		stream.print("<h4>Scenario: " + scenarioName + " (" + configBox + ")</h4>\n"); //$NON-NLS-1$
    425 		stream.print("<a href=\"../"+scenarioFileName+".html\">VIEW GRAPH</a><br><br>\n"); //$NON-NLS-1$
    426 		stream.print("<table><td><b>Current Stream Test Runs</b></td><td><b>Baseline Test Runs</b></td></tr>\n");
    427 		stream.print("<tr valign=\"top\">\n");
    428 		stream.print("<td>");
    429 		currentResultsTable.print();
    430 		stream.print("</td>\n");
    431 		stream.print("<td>");
    432 		baselineResultsTable.print();
    433 		stream.print("</td>\n");
    434 		stream.print("</tr>\n");
    435 		stream.print("</table>\n");
    436 		stream.close();
    437 	}
    438 }
    439 
    440 /*
    441  * Prints a LineGraph object as a gif file.
    442  */
    443 private void saveGraph(LineGraph p, File outputFile) {
    444 	Image image = new Image(Display.getDefault(), GRAPH_WIDTH, GRAPH_HEIGHT);
    445 	p.paint(image);
    446 
    447 	/* Downscale to 8 bit depth palette to save to gif */
    448 	ImageData data = Utils.downSample(image);
    449 	ImageLoader il = new ImageLoader();
    450 	il.data = new ImageData[] { data };
    451 	OutputStream out = null;
    452 	try {
    453 		out = new BufferedOutputStream(new FileOutputStream(outputFile));
    454 		il.save(out, SWT.IMAGE_GIF);
    455 
    456 	} catch (FileNotFoundException e) {
    457 		e.printStackTrace();
    458 	} finally {
    459 		image.dispose();
    460 		if (out != null) {
    461 			try {
    462 				out.close();
    463 			} catch (IOException e1) {
    464 				// silently ignored
    465 			}
    466 		}
    467 	}
    468 }
    469 }
    470