blob: 1dbd52fb094bc225265a387362c27ebcf32d0a39 [file] [log] [blame]
/**********************************************************************
* Copyright (c) 2019 Ericsson
*
* 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.views;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import org.apache.log4j.ConsoleAppender;
import org.apache.log4j.Logger;
import org.apache.log4j.SimpleLayout;
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.finders.UIThreadRunnable;
import org.eclipse.swtbot.swt.finder.junit.SWTBotJunit4ClassRunner;
import org.eclipse.swtbot.swt.finder.utils.SWTBotPreferences;
import org.eclipse.swtbot.swt.finder.waits.DefaultCondition;
import org.eclipse.swtbot.swt.finder.widgets.SWTBotToolbarButton;
import org.eclipse.tracecompass.tmf.core.event.ITmfEvent;
import org.eclipse.tracecompass.tmf.core.exceptions.TmfTraceException;
import org.eclipse.tracecompass.tmf.core.signal.TmfSelectionRangeUpdatedSignal;
import org.eclipse.tracecompass.tmf.core.signal.TmfSignalManager;
import org.eclipse.tracecompass.tmf.core.signal.TmfTraceClosedSignal;
import org.eclipse.tracecompass.tmf.core.signal.TmfTraceOpenedSignal;
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.TmfContext;
import org.eclipse.tracecompass.tmf.core.trace.location.ITmfLocation;
import org.eclipse.tracecompass.tmf.tests.stubs.trace.TmfTraceStub;
import org.eclipse.tracecompass.tmf.ui.swtbot.tests.shared.ConditionHelpers;
import org.eclipse.tracecompass.tmf.ui.swtbot.tests.shared.SWTBotSwtChart;
import org.eclipse.tracecompass.tmf.ui.swtbot.tests.shared.SWTBotUtils;
import org.eclipse.tracecompass.tmf.ui.viewers.xycharts.TmfXYChartViewer;
import org.eclipse.tracecompass.tmf.ui.views.TmfChartView;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.swtchart.Chart;
import org.swtchart.ISeries;
import org.swtchart.ISeriesSet;
/**
* Test for XY Chart views in Trace Compass.
*/
@RunWith(SWTBotJunit4ClassRunner.class)
public class XYChartViewTest {
private static final Logger fLogger = Logger.getRootLogger();
private SWTBotView fViewBot;
private TmfTraceStub fTrace;
private SWTWorkbenchBot fBot;
private TmfXYChartViewer fXyViewer;
private static final TmfTimeRange INITIAL_WINDOW_RANGE = new TmfTimeRange(TmfTimestamp.fromNanos(20), TmfTimestamp.fromNanos(100));
/**
* Set up for test
*/
@BeforeClass
public static void beforeClass() {
SWTBotUtils.initialize();
SWTBotPreferences.TIMEOUT = 20000; /* 20 second timeout */
SWTBotPreferences.KEYBOARD_LAYOUT = "EN_US";
fLogger.removeAllAppenders();
fLogger.addAppender(new ConsoleAppender(new SimpleLayout(), ConsoleAppender.SYSTEM_OUT));
}
/**
* Before the test is run, make the view see the items.
*
* Reset the perspective and close all the views.
*
* @throws TmfTraceException
* could not load a trace
*/
@Before
public void before() throws TmfTraceException {
fBot = new SWTWorkbenchBot();
fBot.closeAllEditors();
for (SWTBotView viewBot : fBot.views()) {
viewBot.close();
}
fTrace = new TmfTraceStub() {
@Override
public @NonNull String getName() {
return "Stub";
}
@Override
public TmfContext seekEvent(ITmfLocation location) {
return new TmfContext();
}
};
fTrace.setStartTime(TmfTimestamp.fromNanos(0));
fTrace.setEndTime(TmfTimestamp.fromNanos(180));
TmfTraceStub trace = fTrace;
trace.initialize(null, "", ITmfEvent.class);
assertNotNull(trace);
// Register trace to trace manager
UIThreadRunnable.syncExec(() -> TmfSignalManager.dispatchSignal(new TmfTraceOpenedSignal(this, trace, null)));
// Open view
SWTBotUtils.openView(XYChartViewStub.ID);
fViewBot = fBot.viewById(XYChartViewStub.ID);
fViewBot.show();
TmfChartView viewPart = (TmfChartView) fViewBot.getViewReference().getView(true);
fXyViewer = viewPart.getChartViewer();
// Wait till SWT chart is constructed
fViewBot.bot().waitUntil(new DefaultCondition() {
@Override
public boolean test() throws Exception {
return fXyViewer.getSwtChart() != null;
}
@Override
public String getFailureMessage() {
return "SWT Chart is null";
}
});
// Wait for trace to be loaded
resetTimeRange();
}
/**
* Clean up after a test, reset the views.
*/
@After
public void after() {
TmfTraceStub trace = fTrace;
assertNotNull(trace);
UIThreadRunnable.syncExec(() -> TmfSignalManager.dispatchSignal(new TmfTraceClosedSignal(this, trace)));
fViewBot.close();
fBot.waitUntil(ConditionHelpers.viewIsClosed(fViewBot));
fTrace.dispose();
}
/**
* Put things back the way they were
*/
@AfterClass
public static void afterClass() {
SWTWorkbenchBot bot = new SWTWorkbenchBot();
bot.closeAllEditors();
fLogger.removeAllAppenders();
}
/**
* Test horizontal zoom, we can see a rounding error
*/
@Test
public void testHorizontalZoom() {
fViewBot.setFocus();
assertEquals(80, fXyViewer.getWindowDuration());
SWTBotSwtChart xyChart = new SWTBotSwtChart(fViewBot.bot());
fireKeyInGraph(xyChart, '=');
fViewBot.bot().waitUntil(new SeriesCondition(fXyViewer, 52));
fireKeyInGraph(xyChart, '+');
fViewBot.bot().waitUntil(new SeriesCondition(fXyViewer, 34));
fireKeyInGraph(xyChart, '-');
fViewBot.bot().waitUntil(new SeriesCondition(fXyViewer, 51));
fireKeyInGraph(xyChart, '-');
fViewBot.bot().waitUntil(new SeriesCondition(fXyViewer, 77));
resetTimeRange();
/* Zoom using zoom-in and zoom-out buttons */
SWTBotToolbarButton button = fViewBot.toolbarButton("Zoom In");
button.click();
fViewBot.bot().waitUntil(new SeriesCondition(fXyViewer, 52));
button = fViewBot.toolbarButton("Zoom Out");
button.click();
fViewBot.bot().waitUntil(new SeriesCondition(fXyViewer, 78));
/*
* Note that 'w' and 's' zooming is based on mouse position. Just check if
* window range was increased or decreased to avoid inaccuracy due to
* the mouse position in test environment.
*/
long previousRange = fXyViewer.getWindowDuration();
fireKeyInGraph(xyChart, 'w');
fViewBot.bot().waitUntil(new SeriesUpdatedCondition(fXyViewer, previousRange, false));
previousRange = fXyViewer.getWindowDuration();
fireKeyInGraph(xyChart, 's');
fViewBot.bot().waitUntil(new SeriesUpdatedCondition(fXyViewer, previousRange, true));
}
/**
* Test zoom to selection
*/
@Test
public void testZoomToSelection() {
SWTBotSwtChart xyChart = new SWTBotSwtChart(fViewBot.bot());
xyChart.setFocus();
assertEquals(80, fXyViewer.getWindowDuration());
/* set selection to trace start time */
ITmfTimestamp selStartTime = TmfTimestamp.fromNanos(30L);
ITmfTimestamp selEndTime = TmfTimestamp.fromNanos(80L);
TmfTimeRange range = new TmfTimeRange(selStartTime, selEndTime);
TmfSignalManager.dispatchSignal(new TmfSelectionRangeUpdatedSignal(this, selStartTime, selEndTime));
// Wait till selection is finished
fViewBot.bot().waitUntil(new DefaultCondition() {
@Override
public boolean test() throws Exception {
return (fXyViewer.getSelectionEndTime() - fXyViewer.getSelectionBeginTime()) == getDuration(range);
}
@Override
public String getFailureMessage() {
return "SWT Chart is null";
}
});
fireKeyInGraph(xyChart, 'z');
fViewBot.bot().waitUntil(new SeriesCondition(fXyViewer, getDuration(range)));
}
/**
* Test 'a' and 'd' navigation
*/
@Test
public void testKeyboardNavigation() {
SWTBotSwtChart xyChart = new SWTBotSwtChart(fViewBot.bot());
xyChart.setFocus();
assertEquals(80, fXyViewer.getWindowDuration());
TmfTimeRange updatedWindowRange = new TmfTimeRange(TmfTimestamp.fromNanos(40), TmfTimestamp.fromNanos(120));
// move to the right
fireKeyInGraph(xyChart, 'd');
fViewBot.bot().waitUntil(new SeriesCondition(fXyViewer, getDuration(updatedWindowRange)));
// move to the left
fireKeyInGraph(xyChart, 'a');
fViewBot.bot().waitUntil(new SeriesCondition(fXyViewer, getDuration(INITIAL_WINDOW_RANGE)));
}
// ------------------------------------------------------------------------
// Helper methods
// ------------------------------------------------------------------------
private static void fireKeyInGraph(SWTBotSwtChart chart, char c, int... modifiers) {
chart.setFocus();
// Move mouse to middle of the chart
chart.moveMouseToWidget();
int mask = 0;
for (int modifier : modifiers) {
mask |= modifier;
}
chart.pressShortcut(mask, c);
}
private static long getDuration(TmfTimeRange range) {
return range.getEndTime().getValue() - range.getStartTime().getValue();
}
private void resetTimeRange() {
TmfWindowRangeUpdatedSignal signal = new TmfWindowRangeUpdatedSignal(this, INITIAL_WINDOW_RANGE);
TmfSignalManager.dispatchSignal(signal);
fViewBot.bot().waitUntil(new SeriesCondition(fXyViewer, getDuration(INITIAL_WINDOW_RANGE)));
}
private static class SeriesCondition extends DefaultCondition {
private TmfXYChartViewer fViewer;
private long fExpectedRange;
private Exception fExecption = null;
public SeriesCondition(TmfXYChartViewer view, long expectedRange) {
fViewer = view;
fExpectedRange = expectedRange;
}
@Override
public boolean test() throws Exception {
try {
Chart chart = fViewer.getSwtChart();
ISeriesSet set = chart.getSeriesSet();
if (set == null) {
return false;
}
ISeries[] series = set.getSeries();
return series != null && series.length > 0 && fExpectedRange == fViewer.getWindowDuration();
} catch (Exception e) {
fExecption = e;
throw e;
}
}
@Override
public String getFailureMessage() {
return "Expected window range (" + fExpectedRange + ") not achieved. Actual=" + fViewer.getWindowDuration() + (fExecption == null ? "" : (". Exception: " + fExecption));
}
}
private class SeriesUpdatedCondition extends DefaultCondition {
TmfXYChartViewer fView;
long fPreviousRange;
boolean fIsIncreased;
private Exception fExecption = null;
public SeriesUpdatedCondition(TmfXYChartViewer view, long previousRange, boolean increased) {
fView = view;
fPreviousRange = previousRange;
fIsIncreased = increased;
}
@Override
public boolean test() throws Exception {
try {
Chart chart = fView.getSwtChart();
ISeriesSet set = chart.getSeriesSet();
if (set == null) {
return false;
}
ISeries[] series = set.getSeries();
if (series == null) {
return false;
}
long newRange = fView.getWindowDuration();
if (fIsIncreased) {
return newRange > fPreviousRange;
}
return newRange < fPreviousRange;
} catch (Exception e) {
fExecption = e;
throw e;
}
}
@Override
public String getFailureMessage() {
return "Window range didn't " + (fIsIncreased ? "increase" : "decrease") +
" (previous: " + fPreviousRange + ", actual: " + fView.getWindowDuration() + ")" + (fExecption == null ? "" : (". Exception: " + fExecption));
}
}
}