Mercurial > hg > release > thermostat-0.7
changeset 742:061618d8bcba
Eclipse Host memory chart
This commit adds a new view in Eclipse for monitoring a host's memory,
similar to the host's Memory tab in the Swing GUI.
Reviewed-by: jerboaa
Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2012-September/003350.html
author | Elliott Baron <ebaron@redhat.com> |
---|---|
date | Thu, 25 Oct 2012 13:35:41 -0400 |
parents | e9531651f79d |
children | 19cb0438ff2e |
files | eclipse/com.redhat.thermostat.eclipse.chart.common/src/com/redhat/thermostat/eclipse/chart/common/HostMemoryViewPart.java eclipse/com.redhat.thermostat.eclipse.chart.common/src/com/redhat/thermostat/eclipse/chart/common/SWTHostMemoryView.java eclipse/com.redhat.thermostat.eclipse.chart.common/src/com/redhat/thermostat/eclipse/chart/common/SWTHostMemoryViewProvider.java eclipse/com.redhat.thermostat.eclipse.test.ui/src/com/redhat/thermostat/eclipse/test/ui/SWTHostMemoryViewTest.java eclipse/com.redhat.thermostat.eclipse.test/src/com/redhat/thermostat/eclipse/test/views/HostMemoryViewPartTest.java |
diffstat | 5 files changed, 1159 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/eclipse/com.redhat.thermostat.eclipse.chart.common/src/com/redhat/thermostat/eclipse/chart/common/HostMemoryViewPart.java Thu Oct 25 13:35:41 2012 -0400 @@ -0,0 +1,72 @@ +/* + * Copyright 2012 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.eclipse.chart.common; + +import com.redhat.thermostat.client.core.views.HostMemoryViewProvider; +import com.redhat.thermostat.client.ui.HostMemoryController; +import com.redhat.thermostat.common.dao.HostInfoDAO; +import com.redhat.thermostat.common.dao.HostRef; +import com.redhat.thermostat.common.dao.MemoryStatDAO; +import com.redhat.thermostat.common.utils.OSGIUtils; +import com.redhat.thermostat.eclipse.views.SWTComponent; + +public class HostMemoryViewPart extends HostRefViewPart { + + private HostMemoryController controller; + + @Override + protected void createControllerView(HostRef ref) { + HostInfoDAO hostInfoDao = OSGIUtils.getInstance().getService( + HostInfoDAO.class); + MemoryStatDAO memoryStatDao = OSGIUtils.getInstance().getService( + MemoryStatDAO.class); + HostMemoryViewProvider viewProvider = OSGIUtils.getInstance() + .getService(HostMemoryViewProvider.class); + controller = createController(hostInfoDao, memoryStatDao, ref, + viewProvider); + SWTComponent view = (SWTComponent) controller.getView(); + view.createControl(top); + } + + public HostMemoryController createController(HostInfoDAO hostInfoDao, + MemoryStatDAO memoryStatDao, HostRef ref, + HostMemoryViewProvider viewProvider) { + return new HostMemoryController(hostInfoDao, memoryStatDao, ref, + viewProvider); + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/eclipse/com.redhat.thermostat.eclipse.chart.common/src/com/redhat/thermostat/eclipse/chart/common/SWTHostMemoryView.java Thu Oct 25 13:35:41 2012 -0400 @@ -0,0 +1,400 @@ +/* + * Copyright 2012 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.eclipse.chart.common; + +import java.awt.Color; +import java.awt.EventQueue; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.CountDownLatch; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.DisposeEvent; +import org.eclipse.swt.events.DisposeListener; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.events.SelectionListener; +import org.eclipse.swt.graphics.Font; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.layout.RowLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Label; +import org.eclipse.ui.PlatformUI; +import org.jfree.chart.ChartFactory; +import org.jfree.chart.JFreeChart; +import org.jfree.chart.axis.NumberAxis; +import org.jfree.chart.renderer.xy.XYItemRenderer; +import org.jfree.data.time.FixedMillisecond; +import org.jfree.data.time.RegularTimePeriod; +import org.jfree.data.time.TimeSeries; +import org.jfree.data.time.TimeSeriesCollection; + +import com.redhat.thermostat.client.core.views.HostMemoryView; +import com.redhat.thermostat.client.locale.LocaleResources; +import com.redhat.thermostat.client.ui.ChartColors; +import com.redhat.thermostat.common.locale.Translate; +import com.redhat.thermostat.common.model.DiscreteTimeData; +import com.redhat.thermostat.common.utils.DisplayableValues; +import com.redhat.thermostat.common.utils.DisplayableValues.Scale; +import com.redhat.thermostat.eclipse.ThermostatConstants; +import com.redhat.thermostat.eclipse.views.SWTComponent; + +public class SWTHostMemoryView extends HostMemoryView implements SWTComponent { + public static final String TEST_ID_TOTAL_MEM = "SWTHostMemoryView.totalMemory"; + public static final String TEST_ID_LEGEND_ITEM_LABEL = "SWTHostMemoryView.legendItemLabel"; + public static final String TEST_ID_LEGEND_ITEM_CHECKBOX = "SWTHostMemoryView.legendItemCheckbox"; + + private static final String LEGEND_COLOUR_BLOCK = "\u2588"; + private static final int H_INDENT = 20; + private static final int SPACER_WIDTH = 10; + private static final Translate<LocaleResources> translator = LocaleResources.createLocalizer(); + + private final TimeSeriesCollection memoryCollection; + private final Map<String, TimeSeries> dataset; + private final Map<String, Color> colors; + private final CopyOnWriteArrayList<GraphVisibilityChangeListener> listeners; + private final Map<String, Composite> checkboxes; + private final CountDownLatch latch; + + private Composite parent; + private Label totalMemory; + private ViewVisibilityWatcher watcher; + private JFreeChart chart; + private Composite legendTop; + + public SWTHostMemoryView() { + this.memoryCollection = new TimeSeriesCollection(); + this.dataset = Collections.synchronizedMap(new HashMap<String, TimeSeries>()); + this.colors = new HashMap<String, Color>(); + this.listeners = new CopyOnWriteArrayList<GraphVisibilityChangeListener>(); + this.checkboxes = new HashMap<String, Composite>(); + this.watcher = new ViewVisibilityWatcher(notifier); + this.latch = new CountDownLatch(1); + this.chart = createMemoryChart(); + } + + public void createControl(Composite parent) { + this.parent = parent; + + Label summaryLabel = new Label(parent, SWT.LEAD); + Font stdFont = summaryLabel.getFont(); + Font boldFont = new Font(stdFont.getDevice(), + stdFont.getFontData()[0].getName(), + stdFont.getFontData()[0].getHeight(), SWT.BOLD); + + summaryLabel.setText(translator.localize(LocaleResources.HOST_MEMORY_SECTION_OVERVIEW)); + summaryLabel.setFont(boldFont); + + Composite detailsTop = new Composite(parent, SWT.NONE); + detailsTop.setLayout(new GridLayout(3, false)); + + Label cpuModelLabel = new Label(detailsTop, SWT.TRAIL); + cpuModelLabel.setText(translator.localize(LocaleResources.HOST_INFO_MEMORY_TOTAL)); + GridData hIndentLayoutData = new GridData(); + hIndentLayoutData.horizontalIndent = H_INDENT; + cpuModelLabel.setLayoutData(hIndentLayoutData); + + Label cpuModelSpacer = new Label(detailsTop, SWT.NONE); + cpuModelSpacer.setLayoutData(new GridData(SPACER_WIDTH, SWT.DEFAULT)); + + totalMemory = new Label(detailsTop, SWT.LEAD); + totalMemory.setData(ThermostatConstants.TEST_TAG, TEST_ID_TOTAL_MEM); + totalMemory.setText("Unknown"); + + Composite chartTop = new RecentTimeSeriesChartComposite(parent, SWT.NONE, chart); + chartTop.setLayout(new GridLayout()); + chartTop.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + + legendTop = new Composite(parent, SWT.NONE); + RowLayout legendLayout = new RowLayout(SWT.HORIZONTAL); + legendLayout.center = true; + legendLayout.wrap = false; + legendLayout.marginHeight = 0; + legendTop.setLayout(legendLayout); + + // Notify threads that controls are created + latch.countDown(); + + // Don't start giving updates until controls are created + watcher.watch(parent, ThermostatConstants.VIEW_ID_HOST_MEMORY); + } + + @Override + public void setTotalMemory(final String newValue) { + PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() { + @Override + public void run() { + if (!totalMemory.isDisposed()) { + totalMemory.setText(newValue); + } + } + }); + } + + private JFreeChart createMemoryChart() { + // FIXME associate a fixed color with each type + + JFreeChart chart = ChartFactory.createTimeSeriesChart( + null, // Title + translator.localize(LocaleResources.HOST_MEMORY_CHART_TIME_LABEL), // x-axis Label + translator.localize(LocaleResources.HOST_MEMORY_CHART_SIZE_LABEL, Scale.MiB.name()), // y-axis Label + memoryCollection, // Dataset + false, // Show Legend + false, // Use tooltips + false // Configure chart to generate URLs? + ); + + chart.getPlot().setBackgroundPaint( new Color(255,255,255,0) ); + chart.getPlot().setBackgroundImageAlpha(0.0f); + chart.getPlot().setOutlinePaint(new Color(0,0,0,0)); + + NumberAxis rangeAxis = (NumberAxis) chart.getXYPlot().getRangeAxis(); + rangeAxis.setAutoRangeMinimumSize(100); + + return chart; + } + + private void fireShowHideHandlers(boolean show, String tag) { + for (GraphVisibilityChangeListener listener: listeners) { + if (show) { + listener.show(tag); + } else { + listener.hide(tag); + } + } + } + + @Override + public void addMemoryChart(final String tag, final String humanReadableName) { + EventQueue.invokeLater(new Runnable() { + @Override + public void run() { + int colorIndex = colors.size(); + colors.put(tag, ChartColors.getColor(colorIndex)); + TimeSeries series = new TimeSeries(tag); + dataset.put(tag, series); + + addLegendItem(tag, humanReadableName); + + updateColors(); + } + }); + } + + @Override + public void removeMemoryChart(final String tag) { + EventQueue.invokeLater(new Runnable() { + @Override + public void run() { + TimeSeries series = dataset.remove(tag); + memoryCollection.removeSeries(series); + + removeLegendItem(tag); + + updateColors(); + } + }); + } + + @Override + public void showMemoryChart(final String tag) { + EventQueue.invokeLater(new Runnable() { + @Override + public void run() { + TimeSeries series = dataset.get(tag); + memoryCollection.addSeries(series); + + updateColors(); + } + }); + } + + @Override + public void hideMemoryChart(final String tag) { + EventQueue.invokeLater(new Runnable() { + @Override + public void run() { + TimeSeries series = dataset.get(tag); + memoryCollection.removeSeries(series); + + updateColors(); + } + }); + } + + @Override + public void addMemoryData(final String tag, List<DiscreteTimeData<? extends Number>> data) { + final List<DiscreteTimeData<? extends Number>> copy = new ArrayList<>(data); + EventQueue.invokeLater(new Runnable() { + @Override + public void run() { + final TimeSeries series = dataset.get(tag); + for (DiscreteTimeData<? extends Number> timeData: copy) { + RegularTimePeriod period = new FixedMillisecond(timeData.getTimeInMillis()); + if (series.getDataItem(period) == null) { + Long sizeInBytes = (Long) timeData.getData(); + Double sizeInMegaBytes = DisplayableValues.Scale.convertTo(Scale.MiB, sizeInBytes); + series.add(new FixedMillisecond(timeData.getTimeInMillis()), sizeInMegaBytes, false); + } + } + series.fireSeriesChanged(); + } + }); + } + + @Override + public void clearMemoryData(final String tag) { + EventQueue.invokeLater(new Runnable() { + @Override + public void run() { + TimeSeries series = dataset.get(tag); + series.clear(); + } + }); + } + + @Override + public void addGraphVisibilityListener(GraphVisibilityChangeListener listener) { + listeners.add(listener); + } + + @Override + public void removeGraphVisibilityListener(GraphVisibilityChangeListener listener) { + listeners.remove(listener); + } + + /** + * Adding or removing series to the series collection may change the order + * of existing items. Plus the paint for the index is now out-of-date. So + * let's walk through all the series and set the right paint for those. + */ + private void updateColors() { + XYItemRenderer itemRenderer = chart.getXYPlot().getRenderer(); + for (int i = 0; i < memoryCollection.getSeriesCount(); i++) { + String tag = (String) memoryCollection.getSeriesKey(i); + Color color = colors.get(tag); + itemRenderer.setSeriesPaint(i, color); + } + } + + private Composite createLabelWithLegend(Composite parent, String text, Color color, final String tag) { + Composite top = new Composite(parent, SWT.NONE); + RowLayout topLayout = new RowLayout(SWT.HORIZONTAL); + topLayout.marginHeight = 0; + topLayout.center = true; + top.setLayout(topLayout); + + final Button checkBox = new Button(top, SWT.CHECK); + checkBox.setData(ThermostatConstants.TEST_TAG, TEST_ID_LEGEND_ITEM_CHECKBOX); + checkBox.addSelectionListener(new SelectionListener() { + + @Override + public void widgetSelected(SelectionEvent e) { + fireShowHideHandlers(checkBox.getSelection(), tag); + } + + @Override + public void widgetDefaultSelected(SelectionEvent e) { + widgetSelected(e); + } + }); + checkBox.setSelection(true); + checkBox.setAlignment(SWT.RIGHT); + + Label colourBlock = new Label(top, SWT.NONE); + colourBlock.setText(LEGEND_COLOUR_BLOCK); + + // Convert to SWT colour + final org.eclipse.swt.graphics.Color swtColour = new org.eclipse.swt.graphics.Color( + PlatformUI.getWorkbench().getDisplay(), color.getRed(), + color.getGreen(), color.getBlue()); + colourBlock.addDisposeListener(new DisposeListener() { + + @Override + public void widgetDisposed(DisposeEvent e) { + swtColour.dispose(); + } + }); + colourBlock.setForeground(swtColour); + + Label colourText = new Label(top, SWT.NONE); + colourText.setData(ThermostatConstants.TEST_TAG, TEST_ID_LEGEND_ITEM_LABEL); + colourText.setText(text); + return top; + } + + private void addLegendItem(final String tag, final String humanReadableName) { + // We need to wait for the controls to be fully constructed + // before modifying the legend + ChartUtils.runAfterCreated(latch, new Runnable() { + @Override + public void run() { + Composite checkbox = createLabelWithLegend(legendTop, + humanReadableName, colors.get(tag), tag); + checkboxes.put(tag, checkbox); + parent.layout(); + } + }); + } + + private void removeLegendItem(final String tag) { + // We need to wait for the controls to be fully constructed + // before modifying the legend + ChartUtils.runAfterCreated(latch, new Runnable() { + @Override + public void run() { + Composite checkbox = checkboxes.remove(tag); + checkbox.dispose(); + parent.layout(); + } + }); + } + + public JFreeChart getChart() { + return chart; + } + + public TimeSeries getSeries(String tag) { + return dataset.get(tag); + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/eclipse/com.redhat.thermostat.eclipse.chart.common/src/com/redhat/thermostat/eclipse/chart/common/SWTHostMemoryViewProvider.java Thu Oct 25 13:35:41 2012 -0400 @@ -0,0 +1,49 @@ +/* + * Copyright 2012 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.eclipse.chart.common; + +import com.redhat.thermostat.client.core.views.HostMemoryView; +import com.redhat.thermostat.client.core.views.HostMemoryViewProvider; + +public class SWTHostMemoryViewProvider implements HostMemoryViewProvider { + + @Override + public HostMemoryView createView() { + return new SWTHostMemoryView(); + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/eclipse/com.redhat.thermostat.eclipse.test.ui/src/com/redhat/thermostat/eclipse/test/ui/SWTHostMemoryViewTest.java Thu Oct 25 13:35:41 2012 -0400 @@ -0,0 +1,541 @@ +/* + * Copyright 2012 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.eclipse.test.ui; + +import static org.junit.Assert.assertEquals; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.Widget; +import org.eclipse.swtbot.eclipse.finder.SWTWorkbenchBot; +import org.eclipse.swtbot.swt.finder.exceptions.WidgetNotFoundException; +import org.eclipse.swtbot.swt.finder.junit.SWTBotJunit4ClassRunner; +import org.eclipse.swtbot.swt.finder.matchers.WithId; +import org.eclipse.swtbot.swt.finder.utils.SWTBotPreferences; +import org.eclipse.swtbot.swt.finder.waits.DefaultCondition; +import org.eclipse.swtbot.swt.finder.widgets.SWTBotCheckBox; +import org.eclipse.swtbot.swt.finder.widgets.SWTBotLabel; +import org.jfree.chart.JFreeChart; +import org.jfree.chart.plot.XYPlot; +import org.jfree.data.time.TimeSeries; +import org.jfree.data.time.TimeSeriesCollection; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import com.redhat.thermostat.client.core.views.HostMemoryView.GraphVisibilityChangeListener; +import com.redhat.thermostat.common.model.DiscreteTimeData; +import com.redhat.thermostat.eclipse.ThermostatConstants; +import com.redhat.thermostat.eclipse.chart.common.SWTHostMemoryView; + +@RunWith(SWTBotJunit4ClassRunner.class) +public class SWTHostMemoryViewTest implements GraphVisibilityChangeListener { + private SWTWorkbenchBot bot; + private SWTHostMemoryView view; + private Shell shell; + + @Before + public void beforeTest() throws Exception { + bot = new SWTWorkbenchBot(); + + Display.getDefault().syncExec(new Runnable() { + + @Override + public void run() { + shell = new Shell(Display.getCurrent()); + Composite parent = new Composite(shell, SWT.NONE); + parent.setLayout(new GridLayout()); + parent.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, + true)); + view = new SWTHostMemoryView(); + view.createControl(parent); + view.addGraphVisibilityListener(SWTHostMemoryViewTest.this); + shell.open(); + } + }); + } + + @After + public void afterTest() throws Exception { + Display.getDefault().syncExec(new Runnable() { + + @Override + public void run() { + if (shell != null) { + shell.close(); + view = null; + } + } + }); + } + + @Test + public void testSetTotalMemory() throws Exception { + String totalMem = "8 GB"; + + view.setTotalMemory(totalMem); + + bot.waitUntil(new DefaultCondition() { + + @Override + public boolean test() throws Exception { + SWTBotLabel label = bot.labelWithId(ThermostatConstants.TEST_TAG, + SWTHostMemoryView.TEST_ID_TOTAL_MEM); + return !label.getText().equals("Unknown"); // TODO Externalize + } + + @Override + public String getFailureMessage() { + return "Total memory label unchanged after set"; + } + + }); + } + + @Test + public void testAddMemoryChart() throws Exception { + final String tag = "TEST"; + String humanReadableName = "Test"; + + addSeries(tag, humanReadableName); + + // Verify legend added + SWTBotLabel label = bot.labelWithId(ThermostatConstants.TEST_TAG, + SWTHostMemoryView.TEST_ID_LEGEND_ITEM_LABEL); + assertEquals(humanReadableName, label.getText()); + } + + @Test + public void testAddMemoryChartMultiple() throws Exception { + final String tag1 = "TEST1"; + final String tag2 = "TEST2"; + String humanReadableName1 = "Test 1"; + String humanReadableName2 = "Test 2"; + + addSeries(tag1, humanReadableName1); + addSeries(tag2, humanReadableName2); + + // Verify legend added + SWTBotLabel label = bot.labelWithId(ThermostatConstants.TEST_TAG, + SWTHostMemoryView.TEST_ID_LEGEND_ITEM_LABEL, 0); + assertEquals(humanReadableName1, label.getText()); + label = bot.labelWithId(ThermostatConstants.TEST_TAG, + SWTHostMemoryView.TEST_ID_LEGEND_ITEM_LABEL, 1); + assertEquals(humanReadableName2, label.getText()); + } + + @Test + public void testRemoveMemoryChart() throws Exception { + final String tag = "TEST"; + String humanReadableName = "Test"; + + addSeries(tag, humanReadableName); + + view.showMemoryChart(tag); + waitUntilSeriesShown(1); + + view.removeMemoryChart(tag); + + checkSeriesRemoved(tag, 0); + + // Verify legend removed + checkLegendRemoved(); + } + + @Test + public void testRemoveMemoryChartMultiple() throws Exception { + final String tag1 = "TEST1"; + final String tag2 = "TEST2"; + String humanReadableName1 = "Test 1"; + String humanReadableName2 = "Test 2"; + + addSeries(tag1, humanReadableName1); + addSeries(tag2, humanReadableName2); + + // Show both series + view.showMemoryChart(tag1); + waitUntilSeriesShown(1); + + view.showMemoryChart(tag2); + waitUntilSeriesShown(2); + + view.removeMemoryChart(tag2); + + checkSeriesRemoved(tag2, 1); + + // Verify legend removed + checkLegendItemRemoved(1); + } + + private void checkSeriesRemoved(final String tag, final int numSeries) { + // Wait until series removed from chart and dataset + bot.waitUntil(new DefaultCondition() { + + @Override + public boolean test() throws Exception { + JFreeChart chart = view.getChart(); + XYPlot plot = chart.getXYPlot(); + int count = plot.getSeriesCount(); + return view.getSeries(tag) == null && count == numSeries; + } + + @Override + public String getFailureMessage() { + return "Data series never removed"; + } + }); + } + + private void checkLegendRemoved() { + bot.waitUntil(new DefaultCondition() { + + @Override + public boolean test() throws Exception { + boolean result = false; + + // Don't make this wait + long saveTimeout = SWTBotPreferences.TIMEOUT; + SWTBotPreferences.TIMEOUT = 0; + try { + bot.widget(WithId.withId(ThermostatConstants.TEST_TAG, + SWTHostMemoryView.TEST_ID_LEGEND_ITEM_LABEL)); + } catch (WidgetNotFoundException e) { + result = true; + } + SWTBotPreferences.TIMEOUT = saveTimeout; + + return result; + } + + @Override + public String getFailureMessage() { + return "Legend not removed"; + } + }); + } + + private void checkLegendItemRemoved(final int size) { + bot.waitUntil(new DefaultCondition() { + + @Override + public boolean test() throws Exception { + List<? extends Widget> widgets = bot.widgets(WithId.withId(ThermostatConstants.TEST_TAG, + SWTHostMemoryView.TEST_ID_LEGEND_ITEM_LABEL)); + return widgets.size() == size; + } + + @Override + public String getFailureMessage() { + return "Legend item not removed"; + } + }); + } + + @Test + public void testHideMemoryChart() throws Exception { + String tag = "TEST"; + String humanReadableName = "Test"; + + addSeries(tag, humanReadableName); + view.showMemoryChart(tag); + + waitUntilSeriesShown(1); + + // Click checkbox to trigger hideMemoryChart + SWTBotCheckBox checkbox = bot.checkBoxWithId( + ThermostatConstants.TEST_TAG, + SWTHostMemoryView.TEST_ID_LEGEND_ITEM_CHECKBOX); + checkbox.click(); + + // Wait until series hidden + waitUntilSeriesShown(0); + } + + @Test + public void testShowMemoryChart() throws Exception { + String tag = "TEST"; + String humanReadableName = "Test"; + + addSeries(tag, humanReadableName); + view.showMemoryChart(tag); + + waitUntilSeriesShown(1); + } + + @Test + public void testShowHiddenMemoryChart() throws Exception { + String tag = "TEST"; + String humanReadableName = "Test"; + + addSeries(tag, humanReadableName); + view.showMemoryChart(tag); + + waitUntilSeriesShown(1); + + // Click checkbox to trigger hideMemoryChart + SWTBotCheckBox checkbox = bot.checkBoxWithId( + ThermostatConstants.TEST_TAG, + SWTHostMemoryView.TEST_ID_LEGEND_ITEM_CHECKBOX); + checkbox.click(); + + // Wait until series hidden + waitUntilSeriesShown(0); + + // Click checkbox to trigger showMemoryChart + checkbox.click(); + + waitUntilSeriesShown(1); + } + + @Test + public void testAddMemoryData() throws Exception { + String tag = "TEST"; + String humanReadableName = "Test"; + + addSeries(tag, humanReadableName); + view.showMemoryChart(tag); + + waitUntilSeriesShown(1); + + // Add some test data + List<DiscreteTimeData<? extends Number>> data = new ArrayList<DiscreteTimeData<? extends Number>>(); + data.add(new DiscreteTimeData<Long>(1000L, 134217728L)); // 128MiB + data.add(new DiscreteTimeData<Long>(2000L, 268435456L)); // 256MiB + data.add(new DiscreteTimeData<Long>(3000L, 536870912L)); // 512MiB + + final JFreeChart chart = view.getChart(); + final TimeSeries series = view.getSeries(tag); + + addData(tag, data, series); + + TimeSeriesCollection dataset = (TimeSeriesCollection) chart.getXYPlot().getDataset(); + int seriesIndex = chart.getXYPlot().getDataset().indexOf(series.getKey()); + + assertEquals(1000L, dataset.getX(seriesIndex, 0)); + assertEquals(2000L, dataset.getX(seriesIndex, 1)); + assertEquals(3000L, dataset.getX(seriesIndex, 2)); + + assertEquals(128D, dataset.getY(seriesIndex, 0)); + assertEquals(256D, dataset.getY(seriesIndex, 1)); + assertEquals(512D, dataset.getY(seriesIndex, 2)); + } + + private void addData(String tag, + final List<DiscreteTimeData<? extends Number>> data, + final TimeSeries series) { + view.addMemoryData(tag, data); + + // Wait until data added to chart + bot.waitUntil(new DefaultCondition() { + + @Override + public boolean test() throws Exception { + return series.getItemCount() == data.size(); + } + + @Override + public String getFailureMessage() { + return "Data never added"; + } + }); + } + + @Test + public void testAddMemoryDataMultiple() throws Exception { + String tag1 = "TEST1"; + String tag2 = "TEST2"; + String humanReadableName1 = "Test 1"; + String humanReadableName2 = "Test 2"; + + addSeries(tag1, humanReadableName1); + view.showMemoryChart(tag1); + + addSeries(tag2, humanReadableName2); + view.showMemoryChart(tag2); + + waitUntilSeriesShown(2); + + // Add some test data + List<DiscreteTimeData<? extends Number>> data1 = new ArrayList<DiscreteTimeData<? extends Number>>(); + data1.add(new DiscreteTimeData<Long>(1000L, 134217728L)); // 128MiB + data1.add(new DiscreteTimeData<Long>(2000L, 268435456L)); // 256MiB + data1.add(new DiscreteTimeData<Long>(3000L, 536870912L)); // 512MiB + + List<DiscreteTimeData<? extends Number>> data2 = new ArrayList<DiscreteTimeData<? extends Number>>(); + data2.add(new DiscreteTimeData<Long>(1500L, 536870912L)); // 512MiB + data2.add(new DiscreteTimeData<Long>(2500L, 134217728L)); // 128MiB + data2.add(new DiscreteTimeData<Long>(3500L, 268435456L)); // 256MiB + + final JFreeChart chart = view.getChart(); + final TimeSeries series1 = view.getSeries(tag1); + final TimeSeries series2 = view.getSeries(tag2); + + addData(tag1, data1, series1); + addData(tag2, data2, series2); + + TimeSeriesCollection dataset = (TimeSeriesCollection) chart.getXYPlot().getDataset(); + int series1Index = chart.getXYPlot().getDataset().indexOf(series1.getKey()); + int series2Index = chart.getXYPlot().getDataset().indexOf(series2.getKey()); + + assertEquals(1000L, dataset.getX(series1Index, 0)); + assertEquals(2000L, dataset.getX(series1Index, 1)); + assertEquals(3000L, dataset.getX(series1Index, 2)); + + assertEquals(128D, dataset.getY(series1Index, 0)); + assertEquals(256D, dataset.getY(series1Index, 1)); + assertEquals(512D, dataset.getY(series1Index, 2)); + + assertEquals(1500L, dataset.getX(series2Index, 0)); + assertEquals(2500L, dataset.getX(series2Index, 1)); + assertEquals(3500L, dataset.getX(series2Index, 2)); + + assertEquals(512D, dataset.getY(series2Index, 0)); + assertEquals(128D, dataset.getY(series2Index, 1)); + assertEquals(256D, dataset.getY(series2Index, 2)); + } + + @Test + public void testClearMemoryData() throws Exception { + String tag1 = "TEST1"; + String tag2 = "TEST2"; + String humanReadableName1 = "Test 1"; + String humanReadableName2 = "Test 2"; + + addSeries(tag1, humanReadableName1); + view.showMemoryChart(tag1); + + addSeries(tag2, humanReadableName2); + view.showMemoryChart(tag2); + + waitUntilSeriesShown(2); + + // Add some test data + List<DiscreteTimeData<? extends Number>> data1 = new ArrayList<DiscreteTimeData<? extends Number>>(); + data1.add(new DiscreteTimeData<Long>(1000L, 134217728L)); // 128MiB + data1.add(new DiscreteTimeData<Long>(2000L, 268435456L)); // 256MiB + data1.add(new DiscreteTimeData<Long>(3000L, 536870912L)); // 512MiB + + List<DiscreteTimeData<? extends Number>> data2 = new ArrayList<DiscreteTimeData<? extends Number>>(); + data2.add(new DiscreteTimeData<Long>(1500L, 536870912L)); // 512MiB + data2.add(new DiscreteTimeData<Long>(2500L, 134217728L)); // 128MiB + data2.add(new DiscreteTimeData<Long>(3500L, 268435456L)); // 256MiB + + final TimeSeries series1 = view.getSeries(tag1); + final TimeSeries series2 = view.getSeries(tag2); + + addData(tag1, data1, series1); + addData(tag2, data2, series2); + + // Remove data from series + view.clearMemoryData(tag1); + + // Wait until data removed to chart + bot.waitUntil(new DefaultCondition() { + + @Override + public boolean test() throws Exception { + return series1.getItemCount() == 0; + } + + @Override + public String getFailureMessage() { + return "Data never added"; + } + }); + + // Check other series' size + assertEquals(data2.size(), series2.getItemCount()); + } + + private void waitUntilSeriesShown(final int numSeries) { + bot.waitUntil(new DefaultCondition() { + + @Override + public boolean test() throws Exception { + JFreeChart chart = view.getChart(); + XYPlot plot = chart.getXYPlot(); + int count = plot.getSeriesCount(); + return count == numSeries; + } + + @Override + public String getFailureMessage() { + return "Data series never shown/hidden"; + } + }); + } + + private void addSeries(final String tag, String humanReadableName) { + view.addMemoryChart(tag, humanReadableName); + + // Wait until series added + bot.waitUntil(new DefaultCondition() { + + @Override + public boolean test() throws Exception { + return view.getSeries(tag) != null; + } + + @Override + public String getFailureMessage() { + return "Data series never added"; + } + }); + + // Wait for legend + bot.labelWithId(ThermostatConstants.TEST_TAG, + SWTHostMemoryView.TEST_ID_LEGEND_ITEM_LABEL); + } + + @Override + public void show(String tag) { + view.showMemoryChart(tag); + } + + @Override + public void hide(String tag) { + view.hideMemoryChart(tag); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/eclipse/com.redhat.thermostat.eclipse.test/src/com/redhat/thermostat/eclipse/test/views/HostMemoryViewPartTest.java Thu Oct 25 13:35:41 2012 -0400 @@ -0,0 +1,97 @@ +/* + * Copyright 2012 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.eclipse.test.views; + +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.same; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.swt.widgets.Composite; +import org.junit.Test; + +import com.redhat.thermostat.client.core.views.BasicView; +import com.redhat.thermostat.client.core.views.HostMemoryViewProvider; +import com.redhat.thermostat.client.ui.HostMemoryController; +import com.redhat.thermostat.common.dao.HostInfoDAO; +import com.redhat.thermostat.common.dao.HostRef; +import com.redhat.thermostat.common.dao.MemoryStatDAO; +import com.redhat.thermostat.eclipse.chart.common.HostMemoryViewPart; +import com.redhat.thermostat.eclipse.chart.common.RefViewPart; +import com.redhat.thermostat.eclipse.chart.common.SWTHostMemoryView; + +public class HostMemoryViewPartTest extends AbstractRefViewPartTest<HostRef> { + + @Test + public void testSelectionAfter() throws Exception { + view.createPartControl(parent); + + HostRef hostRef = new HostRef("TEST", "Test"); + IStructuredSelection selection = mockSelection(hostRef); + view.selectionChanged(hostVMView, selection); + + verify(thermoView).createControl(any(Composite.class)); + } + + @Override + protected void mockController() { + HostMemoryController controller = mock(HostMemoryController.class); + thermoView = mock(SWTHostMemoryView.class); + + HostInfoDAO hostInfoDao = mock(HostInfoDAO.class); + MemoryStatDAO memStatDao = mock(MemoryStatDAO.class); + HostMemoryViewProvider viewProvider = mock(HostMemoryViewProvider.class); + when(osgi.getService(HostInfoDAO.class)).thenReturn(hostInfoDao); + when(osgi.getService(MemoryStatDAO.class)).thenReturn(memStatDao); + when(osgi.getService(HostMemoryViewProvider.class)).thenReturn( + viewProvider); + + doReturn(controller).when(((HostMemoryViewPart) view)) + .createController(same(hostInfoDao), same(memStatDao), + any(HostRef.class), same(viewProvider)); + when(controller.getView()).thenReturn((BasicView) thermoView); + } + + @Override + protected RefViewPart<HostRef> createViewPart() { + return new HostMemoryViewPart(); + } + +}