--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2016 É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 v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.analysis.os.linux.ui.swtbot.tests.perf.views;
+
+import static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
+import java.util.EnumSet;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.swtbot.swt.finder.junit.SWTBotJunit4ClassRunner;
+import org.eclipse.tracecompass.analysis.os.linux.core.cpuusage.KernelCpuUsageAnalysis;
+import org.eclipse.tracecompass.analysis.os.linux.core.inputoutput.InputOutputAnalysisModule;
+import org.eclipse.tracecompass.analysis.os.linux.core.kernel.KernelAnalysisModule;
+import org.eclipse.tracecompass.internal.analysis.os.linux.ui.views.controlflow.ControlFlowView;
+import org.eclipse.tracecompass.internal.analysis.os.linux.ui.views.cpuusage.CpuUsageView;
+import org.eclipse.tracecompass.internal.analysis.os.linux.ui.views.io.diskioactivity.DiskIOActivityView;
+import org.eclipse.tracecompass.internal.analysis.os.linux.ui.views.kernelmemoryusage.KernelMemoryUsageView;
+import org.eclipse.tracecompass.internal.analysis.os.linux.ui.views.resources.ResourcesView;
+import org.eclipse.tracecompass.tmf.core.analysis.IAnalysisModule;
+import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
+import org.eclipse.tracecompass.tmf.ui.swtbot.tests.perf.views.ViewsResponseTest;
+import org.junit.runner.RunWith;
+
+import com.google.common.collect.ImmutableMap;
+
+/**
+ * Test the responsiveness of Control Flow View and Resources View for different
+ * traces and scenarios. Ideally, when running this test, JUL logging should be
+ * enabled using a logger.properties file. LTTng JUL handler is advised since it
+ * works better with multi-threaded applications than other log handlers
+ *
+ * @author Geneviève Bastien
+ */
+@RunWith(SWTBotJunit4ClassRunner.class)
+public abstract class UiResponseTest extends ViewsResponseTest {
+
+ /**
+ * An enumeration of the views available to test
+ */
+ protected enum OsLinuxViews {
+ /**
+ * The Control Flow View
+ */
+ CONTROL_FLOW,
+ /**
+ * The Resources View
+ */
+ RESOURCES,
+ /**
+ * The CPU Usage View
+ */
+ CPU_USAGE,
+ /**
+ * The Disk IO Activity View
+ */
+ DISK_IO_ACTIVITY,
+ /**
+ * The Kernel Memory Usage View
+ */
+ KERNEL_MEMORY_USAGE
+ }
+
+ private static final @NonNull Map<OsLinuxViews, String> VIEW_IDS = ImmutableMap.of(OsLinuxViews.CONTROL_FLOW, ControlFlowView.ID,
+ OsLinuxViews.RESOURCES, ResourcesView.ID,
+ OsLinuxViews.CPU_USAGE, CpuUsageView.ID,
+ OsLinuxViews.DISK_IO_ACTIVITY, DiskIOActivityView.ID,
+ OsLinuxViews.KERNEL_MEMORY_USAGE, KernelMemoryUsageView.ID);
+
+ @Override
+ protected void beforeRunningTest(ITmfTrace trace) {
+ List<IAnalysisModule> modules = new ArrayList<>(3);
+ modules.add(trace.getAnalysisModule(KernelCpuUsageAnalysis.ID));
+ modules.add(trace.getAnalysisModule(KernelAnalysisModule.ID));
+ modules.add(trace.getAnalysisModule(InputOutputAnalysisModule.ID));
+ for (IAnalysisModule module : modules) {
+ if (module != null) {
+ module.schedule();
+ }
+ }
+ for (IAnalysisModule module : modules) {
+ if (module != null) {
+ assertTrue(module.waitForCompletion());
+ }
+ }
+ }
+
+ /**
+ * 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 views
+ * The os linux specific views to test
+ */
+ protected void runTestWithTrace(String tracePath, String traceType, EnumSet<OsLinuxViews> views) {
+ List<String> viewIDs = new ArrayList<>();
+ for (OsLinuxViews view : views) {
+ viewIDs.add(VIEW_IDS.get(view));
+ }
+ runTestWithTrace(tracePath, traceType, viewIDs);
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2016 É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 v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.lttng2.kernel.ui.swtbot.tests.perf;
+
+import java.io.IOException;
+import java.util.EnumSet;
+
+import org.eclipse.core.runtime.FileLocator;
+import org.eclipse.swtbot.swt.finder.junit.SWTBotJunit4ClassRunner;
+import org.eclipse.tracecompass.analysis.os.linux.ui.swtbot.tests.perf.views.UiResponseTest;
+import org.eclipse.tracecompass.internal.lttng2.kernel.ui.views.PerspectiveFactory;
+import org.eclipse.tracecompass.testtraces.ctf.CtfTestTrace;
+import org.eclipse.tracecompass.tmf.ui.swtbot.tests.shared.SWTBotUtils;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test the responsiveness of Control Flow View and Resources View for different
+ * traces and scenarios. Ideally, when running this test, JUL logging should be
+ * enabled using a logger.properties file. LTTng JUL handler is advised since it
+ * works better with multi-threaded applications than other log handlers
+ *
+ * @author Geneviève Bastien
+ */
+@RunWith(SWTBotJunit4ClassRunner.class)
+public class LttngUiResponseTest extends UiResponseTest {
+
+ private static final String TRACE_TYPE = "org.eclipse.linuxtools.lttng2.kernel.tracetype";
+
+ @Override
+ protected void prepareWorkspace() {
+ /* Switch to kernel perspective */
+ SWTBotUtils.switchToPerspective(PerspectiveFactory.ID);
+ }
+
+ /**
+ * Test with the django trace
+ *
+ * @throws SecurityException
+ * If a security manager is present and any the wrong class is
+ * loaded or the class loader is not the same as its ancestor's
+ * loader.
+ * @throws IllegalArgumentException
+ * the object is not the correct class type
+ * @throws IOException
+ * Exceptions with the trace file
+ */
+ @Test
+ public void testWithDjango() throws SecurityException, IllegalArgumentException, IOException {
+ runTestWithTrace(FileLocator.toFileURL(CtfTestTrace.DJANGO_CLIENT.getTraceURL()).getPath(), TRACE_TYPE, EnumSet.allOf(OsLinuxViews.class));
+ }
+
+ /**
+ * Test with the many-threads trace
+ *
+ * @throws SecurityException
+ * If a security manager is present and any the wrong class is
+ * loaded or the class loader is not the same as its ancestor's
+ * loader.
+ * @throws IllegalArgumentException
+ * the object is not the correct class type
+ * @throws IOException
+ * Exceptions with the trace file
+ *
+ */
+ @Test
+ public void testWithManyThreads() throws SecurityException, IllegalArgumentException, IOException {
+ runTestWithTrace(FileLocator.toFileURL(CtfTestTrace.MANY_THREADS.getTraceURL()).getPath(), TRACE_TYPE, EnumSet.of(OsLinuxViews.CONTROL_FLOW, OsLinuxViews.RESOURCES, OsLinuxViews.CPU_USAGE, OsLinuxViews.DISK_IO_ACTIVITY));
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2016 É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 v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.tmf.ui.swtbot.tests.perf.views;
+
+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.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.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 */
+ SWTBotUtils.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.stream().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 = tracePath.substring(tracePath.lastIndexOf(File.separator, tracePath.length() - 2) + 1, tracePath.length() - 1);
+ SWTBotUtils.openTrace(PROJECT_NAME, tracePath, traceType);
+
+ // Make sure all the analyses we'll need are done
+ ITmfTrace trace = TmfTraceManager.getInstance().getActiveTrace();
+ beforeRunningTest(trace);
+ SWTBotUtils.waitForJobs();
+
+ SWTBotView view;
+
+ for (String viewID : viewIDs) {
+ SWTBotUtils.openView(viewID);
+ view = fBot.viewById(viewID);
+ 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";
+ fBot.waitUntil(Conditions.shellIsActive(RENAME_TRACE_DIALOG_TITLE));
+ SWTBotShell shell = fBot.shell(RENAME_TRACE_DIALOG_TITLE);
+ 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) {
+ }
+ SWTBotUtils.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);
+
+ // Set the time range to the full trace range
+ ITmfTrace activeTrace = TmfTraceManager.getInstance().getActiveTrace();
+ TmfTimeRange fullRange = new TmfTimeRange(activeTrace.getStartTime(), activeTrace.getEndTime());
+ TmfSignalManager.dispatchSignal(new TmfWindowRangeUpdatedSignal(this, fullRange));
+ waitViewReady(part, selectionRange, fullRange.getEndTime());
+ 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));
+ windowRange = newWindowRange;
+ waitViewReady(part, selectionRange, newWindowRange.getEndTime());
+ }
+
+ // 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));
+ waitViewReady(part, selectionRange, windowRange.getEndTime());
+
+ // 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));
+ windowRange = newWindowRange;
+ waitViewReady(part, selectionRange, newWindowRange.getEndTime());
+ }
+
+ // 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));
+ waitViewReady(part, selectionRange, windowRange.getEndTime());
+
+ // and zoom out again
+ TmfSignalManager.dispatchSignal(new TmfWindowRangeUpdatedSignal(this, fullRange));
+ waitViewReady(part, selectionRange, fullRange.getEndTime());
+
+ // Reset the original window range
+ TmfSignalManager.dispatchSignal(new TmfWindowRangeUpdatedSignal(this, originalWindowRange));
+ waitViewReady(part, selectionRange, originalWindowRange.getEndTime());
+ }
+
+ private void waitViewReady(IWorkbenchPart part, @NonNull TmfTimeRange selectionRange, @NonNull ITmfTimestamp visibleTime) {
+ if (part instanceof AbstractTimeGraphView) {
+ fBot.waitUntil(ConditionHelpers.timeGraphIsReadyCondition((AbstractTimeGraphView) part, selectionRange, visibleTime));
+ }
+ // TODO Add conditions for other kind of views
+ }
+
+}