You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jmeter.apache.org by fs...@apache.org on 2017/07/16 15:55:58 UTC

svn commit: r1802075 - in /jmeter/trunk: src/components/org/apache/jmeter/visualizers/ViewResultsFullVisualizer.java xdocs/changes.xml

Author: fschumacher
Date: Sun Jul 16 15:55:58 2017
New Revision: 1802075

URL: http://svn.apache.org/viewvc?rev=1802075&view=rev
Log:
Try to keep status of selected and expanded elements in View Results Tree when new elements are added.

Bugzilla Id: 60961

Modified:
    jmeter/trunk/src/components/org/apache/jmeter/visualizers/ViewResultsFullVisualizer.java
    jmeter/trunk/xdocs/changes.xml

Modified: jmeter/trunk/src/components/org/apache/jmeter/visualizers/ViewResultsFullVisualizer.java
URL: http://svn.apache.org/viewvc/jmeter/trunk/src/components/org/apache/jmeter/visualizers/ViewResultsFullVisualizer.java?rev=1802075&r1=1802074&r2=1802075&view=diff
==============================================================================
--- jmeter/trunk/src/components/org/apache/jmeter/visualizers/ViewResultsFullVisualizer.java (original)
+++ jmeter/trunk/src/components/org/apache/jmeter/visualizers/ViewResultsFullVisualizer.java Sun Jul 16 15:55:58 2017
@@ -31,10 +31,16 @@ import java.awt.event.ActionListener;
 import java.awt.event.ItemEvent;
 import java.awt.event.ItemListener;
 import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
+import java.util.Enumeration;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
 
 import javax.swing.BorderFactory;
 import javax.swing.ComboBoxModel;
@@ -53,10 +59,12 @@ import javax.swing.event.TreeSelectionLi
 import javax.swing.tree.DefaultMutableTreeNode;
 import javax.swing.tree.DefaultTreeCellRenderer;
 import javax.swing.tree.DefaultTreeModel;
+import javax.swing.tree.TreeNode;
 import javax.swing.tree.TreePath;
 import javax.swing.tree.TreeSelectionModel;
 
 import org.apache.commons.collections.Buffer;
+import org.apache.commons.collections.EnumerationUtils;
 import org.apache.commons.collections.buffer.CircularFifoBuffer;
 import org.apache.commons.collections.buffer.UnboundedFifoBuffer;
 import org.apache.commons.lang3.StringUtils;
@@ -169,17 +177,32 @@ implements ActionListener, TreeSelection
      * Update the visualizer with new data.
      */
     private void updateGui() {
+        TreePath selectedPath = null;
+        Object oldSelectedElement;
+        Set<Object> oldExpandedElements;
+        Set<TreePath> newExpandedPaths = new HashSet<>();
         synchronized (buffer) {
             if (!dataChanged) {
                 return;
             }
+            
+            final Enumeration<TreePath> expandedElements = jTree.getExpandedDescendants(new TreePath(root));
+            oldExpandedElements = extractExpandedObjects(expandedElements);
+            oldSelectedElement = getSelectedObject();
             root.removeAllChildren();
             for (Object sampler: buffer) {
                 SampleResult res = (SampleResult) sampler;
                 // Add sample
                 DefaultMutableTreeNode currNode = new SearchableTreeNode(res, treeModel);
                 treeModel.insertNodeInto(currNode, root, root.getChildCount());
-                addSubResults(currNode, res);
+                List<TreeNode> path = new ArrayList<>(Arrays.asList(root, currNode));
+                selectedPath = checkExpandedOrSelected(path,
+                        res, oldSelectedElement,
+                        oldExpandedElements, newExpandedPaths, selectedPath);
+                TreePath potentialSelection = addSubResults(currNode, res, path, oldSelectedElement, oldExpandedElements, newExpandedPaths);
+                if (potentialSelection != null) {
+                    selectedPath = potentialSelection;
+                }
                 // Add any assertion that failed as children of the sample node
                 AssertionResult[] assertionResults = res.getAssertionResults();
                 int assertionIndex = currNode.getChildCount();
@@ -187,6 +210,10 @@ implements ActionListener, TreeSelection
                     if (assertionResult.isFailure() || assertionResult.isError()) {
                         DefaultMutableTreeNode assertionNode = new SearchableTreeNode(assertionResult, treeModel);
                         treeModel.insertNodeInto(assertionNode, currNode, assertionIndex++);
+                        selectedPath = checkExpandedOrSelected(path,
+                                assertionResult, oldSelectedElement,
+                                oldExpandedElements, newExpandedPaths, selectedPath,
+                                assertionNode);
                     }
                 }
             }
@@ -197,23 +224,84 @@ implements ActionListener, TreeSelection
         if (root.getChildCount() == 1) {
             jTree.expandPath(new TreePath(root));
         }
+        newExpandedPaths.stream().forEach(jTree::expandPath);
+        if (selectedPath != null) {
+            jTree.setSelectionPath(selectedPath);
+        }
         if (autoScrollCB.isSelected() && root.getChildCount() > 1) {
             jTree.scrollPathToVisible(new TreePath(new Object[] { root,
                     treeModel.getChild(root, root.getChildCount() - 1) }));
         }
     }
 
-    private void addSubResults(DefaultMutableTreeNode currNode, SampleResult res) {
+    private Object getSelectedObject() {
+        Object oldSelectedElement;
+        DefaultMutableTreeNode oldSelectedNode = (DefaultMutableTreeNode) jTree.getLastSelectedPathComponent();
+        oldSelectedElement = oldSelectedNode == null ? null : oldSelectedNode.getUserObject();
+        return oldSelectedElement;
+    }
+
+    private TreePath checkExpandedOrSelected(List<TreeNode> path,
+            Object item, Object oldSelectedObject,
+            Set<Object> oldExpandedObjects, Set<TreePath> newExpandedPaths,
+            TreePath defaultPath) {
+        TreePath result = defaultPath;
+        if (oldSelectedObject == item) {
+            result = toTreePath(path);
+        }
+        if (oldExpandedObjects.contains(item)) {
+            newExpandedPaths.add(toTreePath(path));
+        }
+        return result;
+    }
+
+    private TreePath checkExpandedOrSelected(List<TreeNode> path,
+            Object item, Object oldSelectedObject,
+            Set<Object> oldExpandedObjects, Set<TreePath> newExpandedPaths,
+            TreePath defaultPath, DefaultMutableTreeNode extensionNode) {
+        TreePath result = defaultPath;
+        if (oldSelectedObject == item) {
+            result = toTreePath(path, extensionNode); 
+        }
+        if (oldExpandedObjects.contains(item)) {
+            newExpandedPaths.add(toTreePath(path, extensionNode));
+        }
+        return result;
+    }
+
+    private Set<Object> extractExpandedObjects(final Enumeration<TreePath> expandedElements) {
+        if (expandedElements != null) {
+            @SuppressWarnings("unchecked")
+            final List<TreePath> list = EnumerationUtils.toList(expandedElements);
+            log.debug("Expanded: {}", list);
+            Set<Object> result = list.stream()
+                    .map(TreePath::getLastPathComponent)
+                    .map(c -> (DefaultMutableTreeNode) c)
+                    .map(DefaultMutableTreeNode::getUserObject)
+                    .collect(Collectors.toSet());
+            log.debug("Elements: {}", result);
+            return result;
+        }
+        return Collections.emptySet();
+    }
+
+    private TreePath addSubResults(DefaultMutableTreeNode currNode,
+            SampleResult res, List<TreeNode> path, Object selectedObject,
+            Set<Object> oldExpandedObjects, Set<TreePath> newExpandedPaths) {
         SampleResult[] subResults = res.getSubResults();
 
         int leafIndex = 0;
+        TreePath result = null;
 
         for (SampleResult child : subResults) {
             log.debug("updateGui1 : child sample result - {}", child);
             DefaultMutableTreeNode leafNode = new SearchableTreeNode(child, treeModel);
 
             treeModel.insertNodeInto(leafNode, currNode, leafIndex++);
-            addSubResults(leafNode, child);
+            List<TreeNode> newPath = new ArrayList<>(path);
+            newPath.add(leafNode);
+            result = checkExpandedOrSelected(newPath, child, selectedObject, oldExpandedObjects, newExpandedPaths, result);
+            addSubResults(leafNode, child, newPath, selectedObject, oldExpandedObjects, newExpandedPaths);
             // Add any assertion that failed as children of the sample node
             AssertionResult[] assertionResults = child.getAssertionResults();
             int assertionIndex = leafNode.getChildCount();
@@ -221,9 +309,24 @@ implements ActionListener, TreeSelection
                 if (item.isFailure() || item.isError()) {
                     DefaultMutableTreeNode assertionNode = new SearchableTreeNode(item, treeModel);
                     treeModel.insertNodeInto(assertionNode, leafNode, assertionIndex++);
+                    result = checkExpandedOrSelected(path, item,
+                            selectedObject, oldExpandedObjects, newExpandedPaths, result,
+                            assertionNode);
                 }
             }
         }
+        return result;
+    }
+
+    private TreePath toTreePath(List<TreeNode> newPath) {
+        return new TreePath(newPath.toArray(new TreeNode[newPath.size()]));
+    }
+
+    private TreePath toTreePath(List<TreeNode> path,
+            DefaultMutableTreeNode extensionNode) {
+        TreeNode[] result = path.toArray(new TreeNode[path.size() + 1]);
+        result[result.length - 1] = extensionNode;
+        return new TreePath(result);
     }
 
     /** {@inheritDoc} */

Modified: jmeter/trunk/xdocs/changes.xml
URL: http://svn.apache.org/viewvc/jmeter/trunk/xdocs/changes.xml?rev=1802075&r1=1802074&r2=1802075&view=diff
==============================================================================
--- jmeter/trunk/xdocs/changes.xml [utf-8] (original)
+++ jmeter/trunk/xdocs/changes.xml [utf-8] Sun Jul 16 15:55:58 2017
@@ -171,6 +171,7 @@ Summary
 <ul>
     <li><bug>61005</bug>View Results Tree - Browser Response Data is not clearing</li>
     <li><bug>61121</bug>InfluxdbBackendListenerClient: Only all percentiles are sent, not KO and OK</li>
+    <li><bug>60961</bug>Try to keep status of selected and expanded elements in View Results Tree when new elements are added.</li>
 </ul>
 
 <h3>Timers, Assertions, Config, Pre- &amp; Post-Processors</h3>