blob: 74f6f711ff99cba1a51736c01bd5bc02ed6d1f5e [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2016, 2018 École Polytechnique de Montréal
*
* All rights reserved. This program and the accompanying materials are
* made available under the terms of the Eclipse Public License 2.0 which
* accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*******************************************************************************/
package org.eclipse.tracecompass.tmf.ui.swtbot.tests.perf.views;
import static org.junit.Assert.assertNotNull;
import java.io.File;
import java.util.Collection;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.swtbot.eclipse.finder.SWTWorkbenchBot;
import org.eclipse.swtbot.eclipse.finder.widgets.SWTBotView;
import org.eclipse.swtbot.swt.finder.junit.SWTBotJunit4ClassRunner;
import org.eclipse.swtbot.swt.finder.utils.SWTBotPreferences;
import org.eclipse.swtbot.swt.finder.waits.Conditions;
import org.eclipse.swtbot.swt.finder.widgets.SWTBotShell;
import org.eclipse.swtbot.swt.finder.widgets.SWTBotText;
import org.eclipse.swtbot.swt.finder.widgets.SWTBotTreeItem;
import org.eclipse.test.performance.Dimension;
import org.eclipse.test.performance.Performance;
import org.eclipse.test.performance.PerformanceMeter;
import org.eclipse.tracecompass.tmf.core.signal.TmfSignalManager;
import org.eclipse.tracecompass.tmf.core.signal.TmfWindowRangeUpdatedSignal;
import org.eclipse.tracecompass.tmf.core.timestamp.ITmfTimestamp;
import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimeRange;
import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimestamp;
import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
import org.eclipse.tracecompass.tmf.core.trace.TmfTraceManager;
import org.eclipse.tracecompass.tmf.ui.swtbot.tests.shared.ConditionHelpers;
import org.eclipse.tracecompass.tmf.ui.swtbot.tests.shared.SWTBotUtils;
import org.eclipse.tracecompass.tmf.ui.tests.shared.WaitUtils;
import org.eclipse.tracecompass.tmf.ui.views.TmfChartView;
import org.eclipse.tracecompass.tmf.ui.views.timegraph.AbstractTimeGraphView;
import org.eclipse.ui.IWorkbenchPart;
import org.junit.After;
import org.junit.Before;
import org.junit.runner.RunWith;
/**
* Base class to test the responsiveness of any views. The main method of this
* class, {@link #runTestWithTrace(String, String, Collection)}, will receive a
* collection of view IDs. For each view, the trace under test will be navigated
* when only the view is opened (closing all other listed views, all other
* opened views will remain opened) and also when all the views are opened
* simultaneously. In this last case, it will rename the trace so that events
* from when all the views are opened can be listed separately.
*
* @author Geneviève Bastien
*/
@RunWith(SWTBotJunit4ClassRunner.class)
public abstract class ViewsResponseTest {
private static final String PROJECT_NAME = "test";
private final @NonNull SWTWorkbenchBot fBot = new SWTWorkbenchBot();
/**
* Specific tests will prepare the workspace for the run. For example,
* concrete classes can open perspectives, open views, prepare the layout,
* etc.
*/
protected abstract void prepareWorkspace();
/**
* Things to setup
*/
@Before
public void beforeClass() {
SWTBotUtils.initialize();
Thread.currentThread().setName("SWTBotTest");
/* set up for swtbot */
SWTBotPreferences.TIMEOUT = 60000; /* 60 second timeout */
SWTBotPreferences.KEYBOARD_LAYOUT = "EN_US";
SWTWorkbenchBot bot = new SWTWorkbenchBot();
SWTBotUtils.closeView("welcome", bot);
/* Prepare the workspace */
prepareWorkspace();
/* Finish waiting for eclipse to load */
WaitUtils.waitForJobs();
/* Create project */
SWTBotUtils.createProject(PROJECT_NAME);
}
/**
* Deletes the project from the workspace
*/
@After
public void cleanUp() {
/* Close editors and delete project */
fBot.closeAllEditors();
SWTBotUtils.deleteProject(PROJECT_NAME, fBot);
}
private void closeAllViews(Collection<String> viewIDs) {
viewIDs.forEach(id -> {
SWTBotUtils.openView(id);
SWTBotUtils.closeViewById(id, fBot);
});
}
/**
* This method will be run when all views are still close, but the trace has
* been opened. For instance, if any analysis module need to have completed
* before the test, it can wait for completion here.
*
* @param trace
* The trace used for this test
*/
protected abstract void beforeRunningTest(ITmfTrace trace);
/**
* Run this swtbot with the trace specified at the specified path. The trace
* will be navigate for each view ID separately, then, after renaming the
* trace, with all the views opened. After this test, all views will be
* closed.
*
* @param tracePath
* The full path of the trace to open
* @param traceType
* The trace type of the trace to open
* @param viewIDs
* The IDs of the views to test.
*/
protected void runTestWithTrace(String tracePath, String traceType, Collection<String> viewIDs) {
closeAllViews(viewIDs);
/* Open the trace */
String traceName = new File(tracePath).getName();
SWTBotUtils.openTrace(PROJECT_NAME, tracePath, traceType);
// Make sure all the analyses we'll need are done
ITmfTrace trace = TmfTraceManager.getInstance().getActiveTrace();
beforeRunningTest(trace);
WaitUtils.waitForJobs();
SWTBotView view;
for (String viewID : viewIDs) {
SWTBotUtils.openView(viewID);
view = fBot.viewById(viewID);
prepareView(view);
navigateTrace(view);
SWTBotUtils.closeViewById(viewID, fBot);
}
// Close the trace
fBot.closeAllEditors();
// If there is only 1 view to test, return
if (viewIDs.size() <= 1) {
// Close the views
closeAllViews(viewIDs);
return;
}
// Open all the views
view = null;
for (String viewID : viewIDs) {
SWTBotUtils.openView(viewID);
if (view == null) {
view = fBot.viewById(viewID);
}
}
// Rename the trace, so the results appear under another trace and
// navigate it
renameTrace(traceName, traceName + " full");
navigateTrace(view);
// Close the trace
fBot.closeAllEditors();
// Close the views
closeAllViews(viewIDs);
}
private void renameTrace(String oldName, String newName) {
SWTBotTreeItem traceItem = SWTBotUtils.getTraceProjectItem(fBot, SWTBotUtils.selectTracesFolder(fBot, PROJECT_NAME), oldName);
traceItem.contextMenu().menu("Rename...").click();
final String RENAME_TRACE_DIALOG_TITLE = "Rename Trace";
SWTBotShell shell = fBot.shell(RENAME_TRACE_DIALOG_TITLE).activate();
SWTBotText text = shell.bot().textWithLabel("New Trace name:");
text.setText(newName);
shell.bot().button("OK").click();
fBot.waitUntil(Conditions.shellCloses(shell));
fBot.waitWhile(new ConditionHelpers.ActiveEventsEditor(fBot, null));
SWTBotTreeItem copiedItem = SWTBotUtils.getTraceProjectItem(fBot, SWTBotUtils.selectTracesFolder(fBot, PROJECT_NAME), newName);
copiedItem.contextMenu().menu("Open").click();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
WaitUtils.waitForJobs();
}
// TODO: Add some vertical scrollings. With eventual 2D queries, that will
// be something to test as well
private void navigateTrace(SWTBotView view) {
TmfTimeRange originalWindowRange = TmfTraceManager.getInstance().getCurrentTraceContext().getWindowRange();
TmfTimeRange selectionRange = TmfTraceManager.getInstance().getCurrentTraceContext().getSelectionRange();
IWorkbenchPart part = view.getViewReference().getPart(false);
ITmfTrace activeTrace = TmfTraceManager.getInstance().getActiveTrace();
assertNotNull(activeTrace);
Performance perf = Performance.getDefault();
PerformanceMeter pmBuild = perf.createPerformanceMeter("UI responsiveness: " + activeTrace.getName() + "/" + view.getTitle());
perf.tagAsSummary(pmBuild, view.getTitle() + " CPU", Dimension.CPU_TIME);
// Set the time range to the full trace range
TmfTimeRange fullRange = new TmfTimeRange(activeTrace.getStartTime(), activeTrace.getEndTime());
TmfSignalManager.dispatchSignal(new TmfWindowRangeUpdatedSignal(this, fullRange));
pmBuild.start();
waitViewReady(part, selectionRange, fullRange.getEndTime());
pmBuild.stop();
TmfTimeRange windowRange = fullRange;
// Zoom in 10 times 15 percent of the range and wait for the view to be
// ready
for (int i = 0; i < 10; i++) {
double delta = (windowRange.getEndTime().getValue() - windowRange.getStartTime().getValue()) * 0.15;
TmfTimeRange newWindowRange = new TmfTimeRange(TmfTimestamp.fromNanos((long) (windowRange.getStartTime().toNanos() + delta)), TmfTimestamp.fromNanos((long) (windowRange.getEndTime().toNanos() - delta)));
TmfSignalManager.dispatchSignal(new TmfWindowRangeUpdatedSignal(this, newWindowRange));
pmBuild.start();
windowRange = newWindowRange;
waitViewReady(part, selectionRange, newWindowRange.getEndTime());
pmBuild.stop();
}
// At this zoom level, go to the end
long scrollTime = (windowRange.getEndTime().toNanos() - windowRange.getStartTime().toNanos()) / 2;
windowRange = new TmfTimeRange(TmfTimestamp.fromNanos(fullRange.getEndTime().toNanos() - (2 * scrollTime)), fullRange.getEndTime());
TmfSignalManager.dispatchSignal(new TmfWindowRangeUpdatedSignal(this, windowRange));
pmBuild.start();
waitViewReady(part, selectionRange, windowRange.getEndTime());
pmBuild.stop();
// Scroll back horizontally half the range at a time
for (int i = 0; i < 10; i++) {
TmfTimeRange newWindowRange = new TmfTimeRange(TmfTimestamp.fromNanos(windowRange.getStartTime().toNanos() - scrollTime), TmfTimestamp.fromNanos(windowRange.getEndTime().toNanos() - scrollTime));
TmfSignalManager.dispatchSignal(new TmfWindowRangeUpdatedSignal(this, newWindowRange));
pmBuild.start();
windowRange = newWindowRange;
waitViewReady(part, selectionRange, newWindowRange.getEndTime());
pmBuild.stop();
}
// then go all the way back to the beginning
windowRange = new TmfTimeRange(fullRange.getStartTime(), TmfTimestamp.fromNanos(fullRange.getStartTime().toNanos() + scrollTime));
TmfSignalManager.dispatchSignal(new TmfWindowRangeUpdatedSignal(this, windowRange));
pmBuild.start();
waitViewReady(part, selectionRange, windowRange.getEndTime());
pmBuild.stop();
// and zoom out again
TmfSignalManager.dispatchSignal(new TmfWindowRangeUpdatedSignal(this, fullRange));
pmBuild.start();
waitViewReady(part, selectionRange, fullRange.getEndTime());
pmBuild.stop();
// Reset the original window range
TmfSignalManager.dispatchSignal(new TmfWindowRangeUpdatedSignal(this, originalWindowRange));
pmBuild.start();
waitViewReady(part, selectionRange, originalWindowRange.getEndTime());
pmBuild.stop();
pmBuild.commit();
}
private void waitViewReady(IWorkbenchPart part, @NonNull TmfTimeRange selectionRange, @NonNull ITmfTimestamp visibleTime) {
if (part instanceof AbstractTimeGraphView) {
fBot.waitUntil(ConditionHelpers.timeGraphIsReadyCondition((AbstractTimeGraphView) part, selectionRange, visibleTime));
} else if (part instanceof TmfChartView) {
TmfChartView tmfChartView = (TmfChartView) part;
WaitUtils.waitUntil(cv -> !cv.isDirty(), tmfChartView, "TmfChartView is dirty");
}
// TODO Add conditions for other kind of views
}
/**
* Actions to run on the view before benchmarking
*
* @param view
* view to benchmark
*/
public void prepareView(SWTBotView view) {
// do nothing by default
}
}