/******************************************************************************* * 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.PrintStream; import java.util.List; import java.util.StringTokenizer; import org.eclipse.test.internal.performance.results.db.BuildResults; import org.eclipse.test.internal.performance.results.db.ConfigResults; import org.eclipse.test.internal.performance.results.db.PerformanceResults; import org.eclipse.test.internal.performance.results.db.ScenarioResults; /** * Class used to print a scenario status table. */ public class ScenarioStatusTable { private String component; private PrintStream stream; private int jsIdCount; /** * Creates an HTML table of red x/green check for a scenario for each * configuration. */ public ScenarioStatusTable(String name, PrintStream stream) { this.component = name; this.stream = stream; } /** * Prints the HTML representation of scenario status table into the given stream. */ public void print(PerformanceResults performanceResults) { String baselineName = performanceResults.getBaselineName(); List scenarios = performanceResults.getComponentScenarios(this.component); int size = scenarios.size(); // Print titles printTitle(); this.stream.print("<table border=\"1\">\n"); this.stream.print("<tr>\n"); this.stream.print("<td><h4>All "); this.stream.print(computeSize(scenarios)); this.stream.print(" scenarios</h4></td>\n"); printColumnsTitle(size, performanceResults); // Print one line per scenario results this.jsIdCount = 0; for (int i=0; i<size; i++) { ScenarioResults scenarioResults = (ScenarioResults) scenarios.get(i); if (!scenarioResults.isValid()) continue; this.stream.print("<tr>\n"); this.stream.print("<td>"); boolean hasSummary = scenarioResults.hasSummary(); if (hasSummary) this.stream.print("<b>"); String scenarioBaseline = scenarioResults.getBaselineBuildName(); boolean hasBaseline = baselineName.equals(scenarioBaseline); if (!hasBaseline) { this.stream.print("*"); this.stream.print(scenarioResults.getShortName()); this.stream.print(" <small>(vs. "); this.stream.print(scenarioBaseline); this.stream.print(")</small>"); } else { this.stream.print(scenarioResults.getShortName()); } if (hasSummary) this.stream.print("</b>"); this.stream.print("\n"); String[] configs = performanceResults.getConfigNames(true/*sort*/); int length = configs.length; for (int j=0; j<length; j++) { printConfigStats(scenarioResults, configs[j]); } } this.stream.print("</table>\n"); } private int computeSize(List scenarios) { int size = scenarios.size(); int n = 0; for (int i=0; i<size; i++) { ScenarioResults scenarioResults = (ScenarioResults) scenarios.get(i); if (scenarioResults.isValid()) n++; } return n; } /* * Print the table columns title. */ private void printColumnsTitle(int size, PerformanceResults performanceResults) { String[] configNames = performanceResults.getConfigNames(true/*sort*/); String[] configBoxes = performanceResults.getConfigBoxes(true/*sort*/); int length = configNames.length; for (int i=0; i<length; i++) { String columnTitle = configNames[i]; String boxName = configBoxes[i]; int idx = boxName.indexOf('('); if (idx < 0) { columnTitle = boxName; } else { // first line StringTokenizer tokenizer = new StringTokenizer(boxName.substring(0, idx).trim(), " "); StringBuffer buffer = new StringBuffer(tokenizer.nextToken()); while (tokenizer.hasMoreTokens()) { buffer.append(" "); buffer.append(tokenizer.nextToken()); } buffer.append(' '); // second line tokenizer = new StringTokenizer(boxName.substring(idx).trim(), " "); buffer.append(tokenizer.nextToken()); while (tokenizer.hasMoreTokens()) { buffer.append(" "); buffer.append(tokenizer.nextToken()); } columnTitle = buffer.toString(); } this.stream.print("<td><h5>"); this.stream.print(columnTitle); this.stream.print("</h5>\n"); } } /* * Print the scenario statistics value for the given configuration. */ private void printConfigStats(ScenarioResults scenarioResults, String config) { ConfigResults configResults = scenarioResults.getConfigResults(config); if (configResults == null || !configResults.isValid()) { this.stream.print("<td>n/a</td>"); return; } BuildResults currentBuildResults = configResults.getCurrentBuildResults(); String failure = currentBuildResults.getFailure(); double[] deviation = configResults.getCurrentBuildDeltaInfo(); int confidence = Utils.confidenceLevel(deviation); boolean hasFailure = failure != null; String comment = currentBuildResults.getComment(); String image = Utils.getImage(confidence, hasFailure, comment != null); this.stream.print("<td><a "); if (!hasFailure|| (confidence & Utils.NAN) != 0 || failure.length() == 0){ // write deviation with error in table when test pass this.stream.print("href=\""); this.stream.print(configResults.getName()); this.stream.print('/'); this.stream.print(scenarioResults.getFileName()); this.stream.print(".html\">\n"); this.stream.print("<img hspace=\"10\" border=\"0\" src=\""); this.stream.print(image); this.stream.print("\"/></a>\n"); } else { // create message with tooltip text including deviation with error plus failure message this.jsIdCount+=1; this.stream.print("class=\"tooltipSource\" onMouseover=\"show_element('toolTip"); this.stream.print(this.jsIdCount); this.stream.print("')\" onMouseout=\"hide_element('toolTip"); this.stream.print(this.jsIdCount); this.stream.print("')\" \nhref=\""); this.stream.print(configResults.getName()); this.stream.print('/'); this.stream.print(scenarioResults.getFileName()); this.stream.print(".html\">\n"); this.stream.print("<img hspace=\"10\" border=\"0\" src=\""); this.stream.print(image); this.stream.print("\"/>\n"); this.stream.print("<span class=\"hidden_tooltip\" id=\"toolTip"); this.stream.print(this.jsIdCount); this.stream.print("\">"); this.stream.print(failure); this.stream.print("</span></a>\n"); } String result = Utils.failureMessage(deviation, false); this.stream.print(result); this.stream.print("\n"); } /* * Print the status table explanationtitle. */ private void printTitle() { this.stream.print("<br><h4>Scenario Status</h4>\n"); this.stream.print("The following table gives a complete but compact view of performance results for the component.<br>\n"); this.stream.print("Each line of the table shows the results for one scenario on all machines.<br><br>\n"); this.stream.print("The name of the scenario is in <b>bold</b> when its results are also displayed in the fingerprints<br>\n"); this.stream.print("and starts with an '*' when the scenario has no results in the last baseline run.<br><br>\n"); this.stream.print("Here are information displayed for each test (ie. in each cell):\n"); this.stream.print("<ul>\n"); this.stream.print("<li>an icon showing whether the test fails or passes and whether it's reliable or not.<br>\n"); this.stream.print("The legend for this icon is:\n"); this.stream.print("<ul>\n"); this.stream.print("<li>Green (<img src=\""); this.stream.print(Utils.OK_IMAGE); this.stream.print("\">): mark a <b>successful result</b>, which means this test has neither significant performance regression nor significant standard error</li>"); this.stream.print("<li>Red (<img src=\""); this.stream.print(Utils.FAIL_IMAGE); this.stream.print("\">): mark a <b>failing result</b>, which means this test shows a significant performance regression (more than 10%)</li>\n"); this.stream.print("<li>Gray (<img src=\""); this.stream.print(Utils.FAIL_IMAGE_EXPLAINED); this.stream.print("\">): mark a <b>failing result</b> (see above) with a comment explaining this degradation.</li>\n"); this.stream.print("<li>Yellow (<img src=\""); this.stream.print(Utils.FAIL_IMAGE_WARN); this.stream.print("\"> or <img src=\""); this.stream.print(Utils.OK_IMAGE_WARN); this.stream.print("\">): mark a <b>failing or successful result</b> with a significant standard error (more than "); this.stream.print(Utils.STANDARD_ERROR_THRESHOLD_STRING); this.stream.print(")</li>\n"); this.stream.print("<li>Black (<img src=\""); this.stream.print(Utils.UNKNOWN_IMAGE); this.stream.print("\">): mark an <b>undefined result</b>, which means that deviation on this test is not a number (<code>NaN</code>) or is infinite (happens when the reference value is equals to 0!)</li>"); this.stream.print("<li>\"n/a\": mark a test for with <b>no</b> performance results</li>\n"); this.stream.print("</ul></li>\n"); this.stream.print("<li>the value of the deviation from the baseline as a percentage (ie. formula is: <code>(build_test_time - baseline_test_time) / baseline_test_time</code>)</li>\n"); this.stream.print("<li>the value of the standard error of this deviation as a percentage (ie. formula is: <code>sqrt(build_test_stddev^2 / N + baseline_test_stddev^2 / N) / baseline_test_time</code>)<br>\n"); this.stream.print("When test only has one measure, the standard error cannot be computed and is replaced with a '<font color=\"#CCCC00\">[n/a]</font>'.</li>\n"); this.stream.print("</ul>\n"); this.stream.print("<u>Hints</u>:<ul>\n"); this.stream.print("<li>fly over image of failing tests to see the complete error message</li>\n"); this.stream.print("<li>to look at the complete and detailed test results, click on its image</li>\n"); this.stream.print("</ul>\n"); } }