| /******************************************************************************* |
| * 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 |
| } |
| |
| } |