| /******************************************************************************* |
| * Copyright (c) 2004, 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.ui.tests.performance; |
| |
| import java.text.DecimalFormat; |
| import java.text.NumberFormat; |
| |
| import org.eclipse.core.resources.IProject; |
| import org.eclipse.core.resources.IWorkspace; |
| import org.eclipse.core.resources.ResourcesPlugin; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.Status; |
| import org.eclipse.core.runtime.jobs.Job; |
| import org.eclipse.swt.widgets.Display; |
| import org.eclipse.test.performance.Dimension; |
| import org.eclipse.ui.PlatformUI; |
| import org.eclipse.ui.tests.harness.util.UITestCase; |
| import org.osgi.framework.BundleContext; |
| |
| /** |
| * Baseclass for simple performance tests. |
| * |
| * @since 3.1 |
| */ |
| public abstract class BasicPerformanceTest extends UITestCase { |
| |
| static final public String INTERACTIVE = "org.eclipse.ui.tests.performance.interactive"; |
| |
| public static final int NONE = 0; |
| |
| public static final int LOCAL = 1; |
| |
| public static final int GLOBAL = 2; |
| |
| private PerformanceTester tester; |
| |
| private IProject testProject; |
| |
| final private boolean tagAsGlobalSummary; |
| |
| final private boolean tagAsSummary; |
| |
| private static long startMeasuringTime; |
| |
| private static long stopMeasuringTime; |
| |
| // whether we are displaying iterations per timebox in the console. default is false |
| private static boolean interactive; |
| |
| public BasicPerformanceTest(String testName) { |
| this(testName, NONE); |
| BundleContext context = UIPerformancePlugin.getDefault().getContext(); |
| if (context == null) { // most likely run in a wrong launch mode |
| System.err.println("Unable to retrieve bundle context from BasicPerformanceTest; interactive mode is disabled"); |
| return; |
| } |
| String filterString = context.getProperty(INTERACTIVE); |
| if (filterString != null && filterString.toLowerCase().equals("true")) { |
| interactive = true; |
| } |
| } |
| |
| /** |
| * @param testName |
| */ |
| public BasicPerformanceTest(String testName, int tagging) { |
| super(testName); |
| tagAsGlobalSummary = ((tagging & GLOBAL) != 0); |
| tagAsSummary = ((tagging & LOCAL) != 0); |
| } |
| |
| /** |
| * Answers whether this test should be tagged globally. |
| * |
| * @return whether this test should be tagged globally |
| */ |
| private boolean shouldGloballyTag() { |
| return tagAsGlobalSummary; |
| } |
| |
| /** |
| * Answers whether this test should be tagged locally. |
| * |
| * @return whether this test should be tagged locally |
| */ |
| private boolean shouldLocallyTag() { |
| return tagAsSummary; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.ui.tests.util.UITestCase#doSetUp() |
| */ |
| protected void doSetUp() throws Exception { |
| super.doSetUp(); |
| if (interactive) { |
| return; |
| } |
| tester = new PerformanceTester(this); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.ui.tests.util.UITestCase#doTearDown() |
| */ |
| protected void doTearDown() throws Exception { |
| super.doTearDown(); |
| if (interactive) { |
| return; |
| } |
| tester.dispose(); |
| } |
| |
| protected IProject getProject() { |
| if (testProject == null) { |
| IWorkspace workspace = ResourcesPlugin.getWorkspace(); |
| testProject = workspace.getRoot().getProject( |
| UIPerformanceTestSetup.PROJECT_NAME); |
| } |
| return testProject; |
| } |
| |
| /** |
| * Asserts default properties of the measurements captured for this test |
| * case. |
| * |
| * @throws RuntimeException |
| * if the properties do not hold |
| */ |
| public void assertPerformance() { |
| if (interactive) { |
| return; |
| } |
| tester.assertPerformance(); |
| } |
| |
| /** |
| * Asserts that the measurement specified by the given dimension is within a |
| * certain range with respect to some reference value. If the specified |
| * dimension isn't available, the call has no effect. |
| * |
| * @param dim |
| * the Dimension to check |
| * @param lowerPercentage |
| * a negative number indicating the percentage the measured value |
| * is allowed to be smaller than some reference value |
| * @param upperPercentage |
| * a positive number indicating the percentage the measured value |
| * is allowed to be greater than some reference value |
| * @throws RuntimeException |
| * if the properties do not hold |
| */ |
| public void assertPerformanceInRelativeBand(Dimension dim, |
| int lowerPercentage, int upperPercentage) { |
| if (interactive) { |
| return; |
| } |
| tester.assertPerformanceInRelativeBand(dim, lowerPercentage, |
| upperPercentage); |
| } |
| |
| public void commitMeasurements() { |
| if (interactive) { |
| return; |
| } |
| tester.commitMeasurements(); |
| } |
| |
| /** |
| * Called from within a test case immediately before the code to measure is |
| * run. It starts capturing of performance data. Must be followed by a call |
| * to {@link PerformanceTestCase#stopMeasuring()}before subsequent calls to |
| * this method or {@link PerformanceTestCase#commitMeasurements()}. |
| */ |
| public void startMeasuring() { |
| if (interactive) { |
| startMeasuringTime = System.currentTimeMillis(); |
| return; |
| } |
| tester.startMeasuring(); |
| } |
| |
| public void stopMeasuring() { |
| if (interactive) { |
| stopMeasuringTime = System.currentTimeMillis(); |
| return; |
| } |
| tester.stopMeasuring(); |
| } |
| |
| /** |
| * Mark the scenario of this test case to be included into the global |
| * performance summary. The summary shows the given dimension of the |
| * scenario and labels the scenario with the short name. |
| * |
| * @param shortName |
| * a short (shorter than 40 characters) descritive name of the |
| * scenario |
| * @param dimension |
| * the dimension to show in the summary |
| */ |
| private void tagAsGlobalSummary(String shortName, Dimension dimension) { |
| System.out.println("GLOBAL " + shortName); |
| if (interactive) { |
| return; |
| } |
| tester.tagAsGlobalSummary(shortName, dimension); |
| } |
| |
| private void tagAsSummary(String shortName, Dimension dimension) { |
| System.out.println("LOCAL " + shortName); |
| if (interactive) { |
| return; |
| } |
| tester.tagAsSummary(shortName, dimension); |
| } |
| |
| public void tagIfNecessary(String shortName, Dimension dimension) { |
| if (shouldGloballyTag()) { |
| tagAsGlobalSummary(shortName, dimension); |
| } |
| if (shouldLocallyTag()) { |
| tagAsSummary(shortName, dimension); |
| } |
| } |
| |
| public static void waitForBackgroundJobs() { |
| |
| Job backgroundJob = new Job( |
| "This is a test job which sits around being low priority until everything else finishes") { |
| protected IStatus run(IProgressMonitor monitor) { |
| return Status.OK_STATUS; |
| } |
| }; |
| |
| backgroundJob.setPriority(Job.DECORATE); |
| |
| boolean hadEvents = true; |
| Display display = PlatformUI.getWorkbench().getDisplay(); |
| if (display != null) { |
| while (hadEvents) { |
| hadEvents = false; |
| // Join a low priority job then spin the event loop |
| backgroundJob.schedule(0); |
| try { |
| backgroundJob.join(); |
| } catch (InterruptedException e) { |
| } |
| |
| while (display.readAndDispatch()) { |
| hadEvents = true; |
| } |
| } |
| } |
| } |
| |
| /** |
| * Runs the given runnable until either 100 iterations or 4s has elapsed. |
| * Runs a minimum of 3 times. |
| * |
| * @param runnable |
| * @since 3.1 |
| */ |
| public static void exercise(TestRunnable runnable) throws CoreException { |
| exercise(runnable, 3, 100, 4000); |
| } |
| |
| /** |
| * Exercises the given runnable until either the given number of iterations |
| * or the given amount of time has elapsed, whatever occurs first. |
| * |
| * @param runnable |
| * @param maxIterations |
| * @param maxTime |
| * @since 3.1 |
| */ |
| public static void exercise(TestRunnable runnable, int minIterations, |
| int maxIterations, int maxTime) throws CoreException { |
| if (interactive) { |
| NumberFormat f = new DecimalFormat("##.000"); |
| NumberFormat p = new DecimalFormat("#0.0"); |
| try { |
| runnable.run(); |
| int initialRuns = 3; |
| long startTime = System.currentTimeMillis(); |
| for (int i=0; i<initialRuns; i++) { |
| runnable.run(); |
| } |
| long currentTime = System.currentTimeMillis(); |
| double timePerRun = (currentTime - startTime) / 1000.0 / initialRuns; |
| int totalRuns = initialRuns; |
| double interval = 10.0; // ten seconds |
| long intervalMillis = (long) (1000 * interval); |
| double averagePerInterval = interval/timePerRun; |
| System.out.println("Time per run (roughly): " + f.format(timePerRun) + " - expecting " + f.format(averagePerInterval) + " runs per 10 seconds."); |
| System.err.println("Remember - higher means faster: the following shows number of runs per interval (seconds=" + p.format(interval) + ")."); |
| while (true) { |
| int numOperations = 0; |
| long elapsed = 0; |
| while (elapsed < intervalMillis) { |
| startMeasuringTime = -1; |
| stopMeasuringTime = -1; |
| startTime = System.currentTimeMillis(); |
| numOperations++; |
| runnable.run(); |
| currentTime = System.currentTimeMillis(); |
| if (startMeasuringTime != -1 && stopMeasuringTime != -1) { |
| elapsed += stopMeasuringTime - startMeasuringTime; |
| } else { |
| elapsed += currentTime - startTime; |
| } |
| } |
| timePerRun = elapsed / 1000.0 / numOperations; |
| double operationsPerInterval = interval/timePerRun; |
| double deviation = (operationsPerInterval - averagePerInterval) / averagePerInterval * 100.0; |
| System.out.println(f.format(operationsPerInterval) + " runs/interval (" + (deviation>=0.0?"+":"-") + p.format(Math.abs(deviation)) + "% relative to avg=" + f.format(averagePerInterval) + ")"); |
| averagePerInterval = ((averagePerInterval * totalRuns) + (operationsPerInterval * numOperations)) / (totalRuns + numOperations); |
| totalRuns += numOperations; |
| } |
| } catch(Exception ex) { |
| ex.printStackTrace(); |
| } |
| return; |
| } |
| long startTime = System.currentTimeMillis(); |
| |
| for (int counter = 0; counter < maxIterations; counter++) { |
| |
| try { |
| runnable.run(); |
| } catch (Exception e) { |
| throw new CoreException(new Status(IStatus.ERROR, |
| UIPerformancePlugin.getDefault().getBundle() |
| .getSymbolicName(), IStatus.OK, |
| "An exception occurred", e)); |
| } |
| |
| long curTime = System.currentTimeMillis(); |
| if (curTime - startTime > maxTime && counter >= minIterations - 1) { |
| break; |
| } |
| } |
| } |
| |
| /** |
| * Set the comment for the receiver to string. Note this is added to the |
| * output as is so you will need to add markup if you need a link. |
| * |
| * @param string |
| * The comment to write out for the test. |
| */ |
| public void setDegradationComment(String string) { |
| if (interactive) { |
| return; |
| } |
| tester.setDegradationComment(string); |
| |
| } |
| |
| } |