Mercurial > hg > thermostat-ng > agent
changeset 2479:81cdf24da562
[Byteman] Pre-populate x, y coordinates and filter for graphs.
Reviewed-by: aazores, Alex Macdonald
Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2016-October/021308.html
line wrap: on
line diff
--- a/vm-byteman/client-swing/src/main/java/com/redhat/thermostat/vm/byteman/client/swing/internal/LocaleResources.java Tue Oct 18 09:52:10 2016 -0400 +++ b/vm-byteman/client-swing/src/main/java/com/redhat/thermostat/vm/byteman/client/swing/internal/LocaleResources.java Tue Oct 18 18:34:05 2016 +0200 @@ -57,6 +57,7 @@ IMPORT_RULE, FILTER, FILTER_VALUE_LABEL, + NO_FILTER_NAME, X_COORD, Y_COORD ;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm-byteman/client-swing/src/main/java/com/redhat/thermostat/vm/byteman/client/swing/internal/MetricsKeysAggregator.java Tue Oct 18 18:34:05 2016 +0200 @@ -0,0 +1,68 @@ +/* + * Copyright 2012-2016 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.vm.byteman.client.swing.internal; + +import java.util.ArrayList; +import java.util.List; + +import com.redhat.thermostat.vm.byteman.common.BytemanMetric; +import com.redhat.thermostat.vm.byteman.common.BytemanMetricDataExtractor; + +class MetricsKeysAggregator { + + static List<String> aggregate(List<BytemanMetric> metrics) { + BytemanMetricDataExtractor extractor = new BytemanMetricDataExtractor(); + for (BytemanMetric m: metrics) { + extractor.mineMetric(m); + } + List<String> predefinedKeys = buildPredefinedKeys(); + List<String> userDefinedKeys = extractor.getSortedKeySet(); + // merge lists: predefined keys first, then user-defined keys + List<String> merged = new ArrayList<>(predefinedKeys.size() + userDefinedKeys.size()); + merged.addAll(predefinedKeys); + merged.addAll(userDefinedKeys); + return merged; + } + + private static List<String> buildPredefinedKeys() { + List<String> predefined = new ArrayList<>(PredefinedKeysMapper.PREDEFINED_KEYS.size()); + for (String key: PredefinedKeysMapper.PREDEFINED_KEYS) { + predefined.add(PredefinedKeysMapper.PREDEFINED_PREFIX + key + PredefinedKeysMapper.PREDEFINED_SUFFIX); + } + return predefined; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm-byteman/client-swing/src/main/java/com/redhat/thermostat/vm/byteman/client/swing/internal/PredefinedKeysMapper.java Tue Oct 18 18:34:05 2016 +0200 @@ -0,0 +1,124 @@ +/* + * Copyright 2012-2016 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.vm.byteman.client.swing.internal; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.redhat.thermostat.shared.locale.Translate; + +/** + * Maps and reverse maps predefined keys from the view strings to + * the data model strings. + */ +class PredefinedKeysMapper { + + static enum MapDirection { + VIEW_TO_MODEL, + MODEL_TO_VIEW + } + + private static final Translate<LocaleResources> t = LocaleResources.createLocalizer(); + private static final Map<String, String> PREDEFINED_KEY_MAP; + private static final Map<String, String> REVERSE_PREDEFINED_KEY_MAP; + private static final String EMPTY_STR = ""; + + static final String PREDEFINED_PREFIX = "** "; + static final String PREDEFINED_SUFFIX = " **"; + + // Sorted list of predefined keys + static final List<String> PREDEFINED_KEYS = Collections.unmodifiableList(Arrays.asList( + GraphDataset.FREQUENCY_KEY, GraphDataset.MARKER_KEY, GraphDataset.TIMESTAMP_KEY + )); + static final String NO_FILTER_ITEM = t.localize(LocaleResources.NO_FILTER_NAME).getContents(); + + static { + PREDEFINED_KEY_MAP = new HashMap<>(PREDEFINED_KEYS.size()); + REVERSE_PREDEFINED_KEY_MAP = new HashMap<>(PREDEFINED_KEYS.size()); + for (String key: PREDEFINED_KEYS) { + String mappedValue = PREDEFINED_PREFIX + key + PREDEFINED_SUFFIX; + PREDEFINED_KEY_MAP.put(key, mappedValue); + REVERSE_PREDEFINED_KEY_MAP.put(mappedValue, key); + } + } + + String mapPredefinedKey(String value, MapDirection direction) { + switch (direction) { + case MODEL_TO_VIEW: + return mapValue(PREDEFINED_KEY_MAP, value); + case VIEW_TO_MODEL: + return mapValue(REVERSE_PREDEFINED_KEY_MAP, value); + default: + throw new AssertionError("Unknown direction: " + direction); + } + } + + String mapNoFilter(String value, MapDirection direction) { + switch (direction) { + case MODEL_TO_VIEW: + if (EMPTY_STR.equals(value)) { + return NO_FILTER_ITEM; + } else { + return value; + } + case VIEW_TO_MODEL: + if (NO_FILTER_ITEM.equals(value)) { + return EMPTY_STR; + } else { + return value; + } + default: + throw new AssertionError("Unknown direction: " + direction); + } + } + + private String mapValue(Map<String, String> mapping, String value) { + if (value == null) { + return null; + } + String mappedValue = mapping.get(value); + if (mappedValue != null) { + return mappedValue; + } else { + // return unchanged + return value; + } + } +}
--- a/vm-byteman/client-swing/src/main/java/com/redhat/thermostat/vm/byteman/client/swing/internal/SwingVmBytemanView.java Tue Oct 18 09:52:10 2016 -0400 +++ b/vm-byteman/client-swing/src/main/java/com/redhat/thermostat/vm/byteman/client/swing/internal/SwingVmBytemanView.java Tue Oct 18 18:34:05 2016 +0200 @@ -54,23 +54,28 @@ import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.text.DateFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; import java.util.Date; import java.util.List; +import java.util.Objects; import java.util.concurrent.CopyOnWriteArrayList; import java.util.logging.Level; import java.util.logging.Logger; import javax.swing.JButton; +import javax.swing.JComboBox; import javax.swing.JFileChooser; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JScrollPane; +import javax.swing.JSplitPane; import javax.swing.JTabbedPane; import javax.swing.JTextArea; import javax.swing.JTextField; import javax.swing.JToggleButton; -import javax.swing.JSplitPane; import javax.swing.SwingUtilities; import javax.swing.SwingWorker; import javax.swing.border.LineBorder; @@ -80,6 +85,14 @@ import javax.swing.plaf.basic.BasicSplitPaneDivider; import javax.swing.plaf.basic.BasicSplitPaneUI; +import org.jfree.chart.ChartFactory; +import org.jfree.chart.JFreeChart; +import org.jfree.chart.axis.SymbolAxis; +import org.jfree.chart.plot.PlotOrientation; +import org.jfree.data.category.CategoryDataset; +import org.jfree.data.category.DefaultCategoryDataset; +import org.jfree.data.xy.XYDataset; + import com.redhat.thermostat.client.swing.IconResource; import com.redhat.thermostat.client.swing.SwingComponent; import com.redhat.thermostat.client.swing.components.ActionToggleButton; @@ -101,16 +114,9 @@ import com.redhat.thermostat.shared.locale.LocalizedString; import com.redhat.thermostat.shared.locale.Translate; import com.redhat.thermostat.vm.byteman.client.swing.internal.GraphDataset.CoordinateType; +import com.redhat.thermostat.vm.byteman.client.swing.internal.PredefinedKeysMapper.MapDirection; import com.redhat.thermostat.vm.byteman.common.BytemanMetric; -import org.jfree.chart.ChartFactory; -import org.jfree.chart.JFreeChart; -import org.jfree.chart.axis.SymbolAxis; -import org.jfree.chart.plot.PlotOrientation; -import org.jfree.data.category.CategoryDataset; -import org.jfree.data.category.DefaultCategoryDataset; -import org.jfree.data.xy.XYDataset; - public class SwingVmBytemanView extends VmBytemanView implements SwingComponent { private static final Logger logger = LoggingUtils.getLogger(SwingVmBytemanView.class); @@ -121,6 +127,7 @@ private static final Icon ARROW_RIGHT = IconResource.ARROW_RIGHT.getIcon(); private static final String EMPTY_STR = ""; private static final String BYTEMAN_CHART_LABEL = EMPTY_STR; + private static final PredefinedKeysMapper KEYS_MAPPER = new PredefinedKeysMapper(); static final String NO_METRICS_AVAILABLE = t.localize(LocaleResources.NO_METRICS_AVAILABLE).getContents(); @@ -158,6 +165,12 @@ private String filter = null; private String value = null; private String graphtype = null; + + // Graph widgets + private final JComboBox<String> xCombo; + private final JComboBox<String> yCombo; + private final JComboBox<String> filterCombo; + private final JTextField filterText; // duration over which to search for metrics private Duration duration = ThermostatChartPanel.DEFAULT_DATA_DISPLAY; @@ -380,41 +393,28 @@ // insert two labelled text fields to allow axis selection JLabel xlabel = new JLabel(t.localize(LocaleResources.X_COORD).getContents()); JLabel ylabel = new JLabel(t.localize(LocaleResources.Y_COORD).getContents()); - final JTextField xtext = new JTextField(30); - final JTextField ytext = new JTextField(30); - - xtext.getDocument().addDocumentListener(new javax.swing.event.DocumentListener() { - + final List<BytemanMetric> emptyBytemanMetrics = Collections.emptyList(); + String[] keyItems = MetricsKeysAggregator.aggregate(emptyBytemanMetrics).toArray(new String[0]); + xCombo = new JComboBox<>(keyItems); + xCombo.addActionListener(new java.awt.event.ActionListener() { + @Override - public void insertUpdate(DocumentEvent e) - { - xkey = xtext.getText(); - } - @Override - public void removeUpdate(DocumentEvent e) - { - xkey = xtext.getText(); - } - @Override - public void changedUpdate(DocumentEvent e) - { - xkey = xtext.getText(); + public void actionPerformed(java.awt.event.ActionEvent e) { + @SuppressWarnings("unchecked") + JComboBox<String> combo = (JComboBox<String>)e.getSource(); + String candidate = (String)combo.getSelectedItem(); + xkey = KEYS_MAPPER.mapPredefinedKey(candidate, MapDirection.VIEW_TO_MODEL); } }); - - ytext.getDocument().addDocumentListener(new javax.swing.event.DocumentListener() { - + yCombo = new JComboBox<>(keyItems); + yCombo.addActionListener(new java.awt.event.ActionListener() { + @Override - public void insertUpdate(DocumentEvent e) { - ykey = ytext.getText(); - } - @Override - public void removeUpdate(DocumentEvent e) { - ykey = ytext.getText(); - } - @Override - public void changedUpdate(DocumentEvent e) { - ykey = ytext.getText(); + public void actionPerformed(java.awt.event.ActionEvent e) { + @SuppressWarnings("unchecked") + JComboBox<String> combo = (JComboBox<String>)e.getSource(); + String candidate = (String)combo.getSelectedItem(); + ykey = KEYS_MAPPER.mapPredefinedKey(candidate, MapDirection.VIEW_TO_MODEL); } }); // insert button to initiate graph redraw @@ -430,39 +430,32 @@ JLabel filterlabel = new JLabel(t.localize(LocaleResources.FILTER).getContents()); JLabel valuelabel = new JLabel(t.localize(LocaleResources.FILTER_VALUE_LABEL).getContents()); - final JTextField filterText = new JTextField(30); - final JTextField valueText = new JTextField(30); - + String[] filterKeyVals = buildFilterKeyVals(keyItems); + filterCombo = new JComboBox<>(filterKeyVals); + filterCombo.addActionListener(new java.awt.event.ActionListener() { + + @Override + public void actionPerformed(java.awt.event.ActionEvent e) { + @SuppressWarnings("unchecked") + JComboBox<String> combo = (JComboBox<String>)e.getSource(); + String candidate = (String)combo.getSelectedItem(); + filter = mapFilter(candidate, MapDirection.VIEW_TO_MODEL); + updateFilterText(filter); + } + }); + filterText = new JTextField(30); filterText.getDocument().addDocumentListener(new javax.swing.event.DocumentListener() { - @Override public void insertUpdate(DocumentEvent e) { - filter = filterText.getText(); + value = filterText.getText(); } @Override public void removeUpdate(DocumentEvent e) { - filter = filterText.getText(); - } - @Override - public void changedUpdate(DocumentEvent e) - { - filter = filterText.getText(); - } - }); - - valueText.getDocument().addDocumentListener(new javax.swing.event.DocumentListener() { - - @Override - public void insertUpdate(DocumentEvent e) { - value = valueText.getText(); - } - @Override - public void removeUpdate(DocumentEvent e) { - value = valueText.getText(); + value = filterText.getText(); } @Override public void changedUpdate(DocumentEvent e) { - value = valueText.getText(); + value = filterText.getText(); } }); @@ -484,7 +477,7 @@ graphConstraints.weighty = 0.5; graphConstraints.weightx = weightTextBox; graphConstraints.insets = spacerLeftInsets; - graphControlHolder.add(xtext, graphConstraints); + graphControlHolder.add(xCombo, graphConstraints); graphConstraints.fill = GridBagConstraints.HORIZONTAL; graphConstraints.gridx = 2; graphConstraints.gridy = 0; @@ -498,7 +491,7 @@ graphConstraints.weighty = 0.5; graphConstraints.weightx = weightTextBox; graphConstraints.insets = spacerLeftInsets; - graphControlHolder.add(ytext, graphConstraints); + graphControlHolder.add(yCombo, graphConstraints); graphConstraints.fill = GridBagConstraints.HORIZONTAL; graphConstraints.gridx = 0; graphConstraints.gridy = 1; @@ -512,7 +505,7 @@ graphConstraints.weighty = 0.5; graphConstraints.weightx = weightTextBox; graphConstraints.insets = spacerLeftInsets; - graphControlHolder.add(filterText, graphConstraints); + graphControlHolder.add(filterCombo, graphConstraints); graphConstraints.fill = GridBagConstraints.HORIZONTAL; graphConstraints.gridx = 2; graphConstraints.gridy = 1; @@ -526,7 +519,7 @@ graphConstraints.weighty = 0.5; graphConstraints.weightx = weightTextBox; graphConstraints.insets = spacerLeftInsets; - graphControlHolder.add(valueText, graphConstraints); + graphControlHolder.add(filterText, graphConstraints); graphConstraints.fill = GridBagConstraints.HORIZONTAL; graphConstraints.gridx = 4; graphConstraints.gridy = 1; @@ -579,6 +572,25 @@ mainContainer.addToolBarButton(toggleButton); } + /* + * When there is no key selected to filter by, don't enable the value + * input text box as this doesn't make sense. + */ + private void updateFilterText(String filterValue) { + if (filterValue != null && !filterValue.isEmpty()) { + filterText.setEnabled(true); + } else { + filterText.setEnabled(false); + } + } + + private String[] buildFilterKeyVals(String[] keyItems) { + List<String> filterKeys = new ArrayList<>(keyItems.length + 1); + filterKeys.add(PredefinedKeysMapper.NO_FILTER_ITEM); // default to no filter + filterKeys.addAll(Arrays.asList(keyItems)); + return filterKeys.toArray(new String[] {}); + } + private void updateGraphControlPanel(double weightx, double weighty) { GridBagConstraints c = new GridBagConstraints(); c.fill = GridBagConstraints.BOTH; @@ -853,16 +865,51 @@ final String f = filter; final String v = value; final String t = graphtype; + final String[] keyItems = MetricsKeysAggregator.aggregate(metrics).toArray(new String[] {}); SwingUtilities.invokeLater(new Runnable() { @Override public void run() { + updateComboKeyItems(keyItems, xk, yk, f); GraphDataset dataset = makeGraphDataset(ms, xk, yk, f, v); if (dataset != null) { switchGraph(dataset, t); } } + }); } + + private void updateComboKeyItems(String[] keyItems, String xkey, String ykey, String filter) { + xCombo.removeAllItems(); + yCombo.removeAllItems(); + filterCombo.removeAllItems(); + filterCombo.addItem(PredefinedKeysMapper.NO_FILTER_ITEM); // allow for no filter + for (String key: keyItems) { + xCombo.addItem(key); + yCombo.addItem(key); + filterCombo.addItem(key); + } + String xSelection = KEYS_MAPPER.mapPredefinedKey(xkey, MapDirection.MODEL_TO_VIEW); + selectItem(xCombo, xSelection); + String ySelection = KEYS_MAPPER.mapPredefinedKey(ykey, MapDirection.MODEL_TO_VIEW); + selectItem(yCombo, ySelection); + String filterSelection = mapFilter(filter, MapDirection.MODEL_TO_VIEW); + selectItem(filterCombo, filterSelection); + } + + private String mapFilter(String filter, MapDirection direction) { + String mapped = KEYS_MAPPER.mapPredefinedKey(filter, direction); + if (!Objects.equals(filter, mapped)) { + return mapped; + } + return KEYS_MAPPER.mapNoFilter(filter, direction); + } + + private void selectItem(final JComboBox<String> combo, String selection) { + if (selection != null) { + combo.setSelectedItem(selection); + } + } private GraphDataset makeGraphDataset(List<BytemanMetric> metrics, String xkey, String ykey, String filter, String value) { GraphDataset dataset = new GraphDataset(metrics, xkey, ykey, filter, value);
--- a/vm-byteman/client-swing/src/main/resources/com/redhat/thermostat/vm/byteman/client/swing/internal/strings.properties Tue Oct 18 09:52:10 2016 -0400 +++ b/vm-byteman/client-swing/src/main/resources/com/redhat/thermostat/vm/byteman/client/swing/internal/strings.properties Tue Oct 18 18:34:05 2016 +0200 @@ -15,5 +15,6 @@ IMPORT_RULE = Import Rule from File FILTER = Filter: FILTER_VALUE_LABEL = == +NO_FILTER_NAME = <No Filter> X_COORD = x: Y_COORD = y:
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm-byteman/client-swing/src/test/java/com/redhat/thermostat/vm/byteman/client/swing/internal/MetricsKeysAggregatorTest.java Tue Oct 18 18:34:05 2016 +0200 @@ -0,0 +1,86 @@ +/* + * Copyright 2012-2016 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.vm.byteman.client.swing.internal; + +import static org.junit.Assert.assertEquals; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.junit.Test; + +import com.redhat.thermostat.vm.byteman.common.BytemanMetric; + +public class MetricsKeysAggregatorTest { + + @Test + public void testEmptyUserDefined() { + List<BytemanMetric> metrics = Collections.emptyList(); + List<String> actual = MetricsKeysAggregator.aggregate(metrics); + assertEquals("3 predefined keys", 3, actual.size()); + String first = PredefinedKeysMapper.PREDEFINED_PREFIX + GraphDataset.FREQUENCY_KEY + PredefinedKeysMapper.PREDEFINED_SUFFIX; + String second = PredefinedKeysMapper.PREDEFINED_PREFIX + GraphDataset.MARKER_KEY + PredefinedKeysMapper.PREDEFINED_SUFFIX; + String third = PredefinedKeysMapper.PREDEFINED_PREFIX + GraphDataset.TIMESTAMP_KEY + PredefinedKeysMapper.PREDEFINED_SUFFIX; + assertEquals(first, actual.get(0)); + assertEquals(second, actual.get(1)); + assertEquals(third, actual.get(2)); + } + + @Test + public void testMergedNonEmptyUserDefined() { + String[] keys = new String[] { + "foo", "bar", "baz" + }; + List<BytemanMetric> metrics = buildMetrics(keys); + List<String> actual = MetricsKeysAggregator.aggregate(metrics); + assertEquals("3 predefined + 3 user-defined keys", 6, actual.size()); + assertEquals(keys[1], actual.get(3)); + assertEquals(keys[2], actual.get(4)); + assertEquals(keys[0], actual.get(5)); + } + + private List<BytemanMetric> buildMetrics(String[] keys) { + List<BytemanMetric> metrics = new ArrayList<>(); + for (String key: keys) { + BytemanMetric m = new BytemanMetric(); + m.setData("{\"" + key + "\": \"value\"}"); + metrics.add(m); + } + return metrics; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm-byteman/client-swing/src/test/java/com/redhat/thermostat/vm/byteman/client/swing/internal/PredefinedKeysMapperTest.java Tue Oct 18 18:34:05 2016 +0200 @@ -0,0 +1,131 @@ +/* + * Copyright 2012-2016 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.vm.byteman.client.swing.internal; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; + +import org.junit.Before; +import org.junit.Test; + +import com.redhat.thermostat.vm.byteman.client.swing.internal.PredefinedKeysMapper.MapDirection; + +public class PredefinedKeysMapperTest { + + private PredefinedKeysMapper mapper; + + @Before + public void setup() { + mapper = new PredefinedKeysMapper(); + } + + @Test + public void testMapKeysNull() { + String mapped = mapper.mapPredefinedKey(null, MapDirection.MODEL_TO_VIEW); + assertNull(mapped); + mapped = mapper.mapPredefinedKey(null, MapDirection.VIEW_TO_MODEL); + assertNull(mapped); + } + + @Test + public void testMapNoFilterNull() { + String mapped = mapper.mapNoFilter(null, MapDirection.MODEL_TO_VIEW); + assertNull(mapped); + mapped = mapper.mapNoFilter(null, MapDirection.VIEW_TO_MODEL); + assertNull(mapped); + } + + /** + * Only empty string and localized {@code No Filter} get mapped. + */ + @Test + public void testMapNoFilterUntracked() { + String filterValUntracked = "foobar"; + assertFalse("Precondition not met", PredefinedKeysMapper.NO_FILTER_ITEM.equals(filterValUntracked)); + String mapped = mapper.mapNoFilter(filterValUntracked, MapDirection.MODEL_TO_VIEW); + assertSame(filterValUntracked, mapped); + mapped = mapper.mapNoFilter(filterValUntracked, MapDirection.VIEW_TO_MODEL); + assertSame(filterValUntracked, mapped); + } + + /** + * Only predefined keys shall map. Others should get returned verbatim. + */ + @Test + public void testMapPredefinedKeysUntracked() { + String keyValUntracked = "foobar"; + String mapped = mapper.mapPredefinedKey(keyValUntracked, MapDirection.MODEL_TO_VIEW); + assertSame(keyValUntracked, mapped); + mapped = mapper.mapPredefinedKey(keyValUntracked, MapDirection.VIEW_TO_MODEL); + assertSame(keyValUntracked, mapped); + } + + @Test + public void testMapFromModelPredefinedKeys() { + for (String keyModel: PredefinedKeysMapper.PREDEFINED_KEYS) { + String mapped = mapper.mapPredefinedKey(keyModel, MapDirection.MODEL_TO_VIEW); + String expected = PredefinedKeysMapper.PREDEFINED_PREFIX + keyModel + PredefinedKeysMapper.PREDEFINED_SUFFIX; + assertEquals(expected, mapped); + } + } + + @Test + public void testMapFromViewPredefinedKeys() { + for (String keyModel: PredefinedKeysMapper.PREDEFINED_KEYS) { + String preMapping = PredefinedKeysMapper.PREDEFINED_PREFIX + keyModel + PredefinedKeysMapper.PREDEFINED_SUFFIX; + String mapped = mapper.mapPredefinedKey(preMapping, MapDirection.VIEW_TO_MODEL); + assertEquals(keyModel, mapped); + } + } + + @Test + public void testMapFromModelNoFilter() { + String noFilterModel = ""; + String mapped = mapper.mapNoFilter(noFilterModel, MapDirection.MODEL_TO_VIEW); + assertEquals(PredefinedKeysMapper.NO_FILTER_ITEM, mapped); + } + + @Test + public void testMapFromViewNoFilter() { + String noFilterView = PredefinedKeysMapper.NO_FILTER_ITEM; + String mapped = mapper.mapNoFilter(noFilterView, MapDirection.VIEW_TO_MODEL); + String noFilterModelExpected = ""; + assertEquals(noFilterModelExpected, mapped); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm-byteman/common/src/main/java/com/redhat/thermostat/vm/byteman/common/BytemanMetricDataExtractor.java Tue Oct 18 18:34:05 2016 +0200 @@ -0,0 +1,75 @@ +/* + * Copyright 2012-2016 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.vm.byteman.common; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; + +public class BytemanMetricDataExtractor { + + private final Set<String> keySet; + + public BytemanMetricDataExtractor() { + this.keySet = new HashSet<>(); + } + + public void mineMetric(BytemanMetric m) { + Objects.requireNonNull(m); + Map<String, Object> dataMap = m.getDataAsMap(); + if (dataMap != null) { + for (String key: dataMap.keySet()) { + keySet.add(key); + } + } + } + + /** + * + * @return A sorted list of the set of keys. + */ + public List<String> getSortedKeySet() { + List<String> returnedList = new ArrayList<>(keySet); + Collections.sort(returnedList); + return returnedList; + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm-byteman/common/src/test/java/com/redhat/thermostat/vm/byteman/common/BytemanMetricDataExtractorTest.java Tue Oct 18 18:34:05 2016 +0200 @@ -0,0 +1,140 @@ +/* + * Copyright 2012-2016 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.vm.byteman.common; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.Before; +import org.junit.Test; + +public class BytemanMetricDataExtractorTest { + + private BytemanMetricDataExtractor extractor; + + @Before + public void setup() { + extractor = new BytemanMetricDataExtractor(); + } + + @Test + public void testEmptyKeySet() { + List<String> actual = extractor.getSortedKeySet(); + assertTrue(actual.isEmpty()); + } + + @Test + public void testAdditiveKeySet() { + String firstKey = "zoo"; + BytemanMetric first = new BytemanMetric(); + first.setData("{\"" + firstKey + "\": \"bar\"}"); + extractor.mineMetric(first); + List<String> actual = extractor.getSortedKeySet(); + assertEquals(1, actual.size()); + assertEquals(firstKey, actual.get(0)); + + BytemanMetric second = new BytemanMetric(); + String secondKey = "secondKey"; + second.setData("{\"" + secondKey + "\": \"baz\"}"); + extractor.mineMetric(second); + actual = extractor.getSortedKeySet(); + assertEquals(2, actual.size()); + + // verify sorting + assertEquals(secondKey, actual.get(0)); + assertEquals(firstKey, actual.get(1)); + } + + @Test + public void testDuplicateKey() { + String firstKey = "zoo"; + BytemanMetric first = new BytemanMetric(); + first.setData("{\"" + firstKey + "\": \"bar\"}"); + extractor.mineMetric(first); + List<String> actual = extractor.getSortedKeySet(); + assertEquals(1, actual.size()); + assertEquals(firstKey, actual.get(0)); + + extractor.mineMetric(first); + actual = extractor.getSortedKeySet(); + assertEquals(1, actual.size()); + assertEquals(firstKey, actual.get(0)); + } + + @Test + public void extractionDoesNotFailIfNoData() { + BytemanMetric m = new BytemanMetric(); + extractor.mineMetric(m); // this shall not fail with NPE + List<String> actual = extractor.getSortedKeySet(); + assertTrue(actual.isEmpty()); + } + + @Test(expected = NullPointerException.class) + public void mineRequiresNonNull() { + extractor.mineMetric(null); + } + + @Test + public void canExtractListOfMetrics() { + String[] keys = new String[] { + "001_first", "002_second", "003_third", "004_fourth" + }; + List<BytemanMetric> mList = buildMetrics(keys); + for (BytemanMetric metric: mList) { + extractor.mineMetric(metric); + } + List<String> keySet = extractor.getSortedKeySet(); + assertEquals(4, keySet.size()); + for (int i = 0; i < keySet.size(); i++) { + assertEquals(keys[0], keySet.get(0)); + } + + } + + private List<BytemanMetric> buildMetrics(String[] keys) { + List<BytemanMetric> list = new ArrayList<>(); + for (String key: keys) { + BytemanMetric m = new BytemanMetric(); + m.setData("{\"" + key + "\": \"" + key + "_value\"}"); + list.add(m); + } + return list; + } +}