changeset 1986:38e39491d46a

Fix heap dump details tab pane PR3059, PR3022 Reviewed-by: jkang Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2016-June/019844.html Original-thread: http://icedtea.classpath.org/pipermail/thermostat/2015-October/016799.html
author James Aziz <jaziz@redhat.com>
date Wed, 29 Jun 2016 12:23:10 -0400
parents c7d7084f1ed5
children 0abb727993f5
files vm-heap-analysis/client-core/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/core/HeapDumpDetailsView.java vm-heap-analysis/client-core/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/core/internal/HeapDumpController.java vm-heap-analysis/client-core/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/core/internal/HeapDumpDetailsController.java vm-heap-analysis/client-core/src/test/java/com/redhat/thermostat/vm/heap/analysis/client/core/internal/HeapDumpControllerTest.java vm-heap-analysis/client-core/src/test/java/com/redhat/thermostat/vm/heap/analysis/client/core/internal/HeapDumpDetailsControllerTest.java vm-heap-analysis/client-swing/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/swing/internal/HeapDetailsSwing.java vm-heap-analysis/client-swing/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/swing/internal/SwingHeapTreeMapView.java vm-heap-analysis/client-swing/src/test/java/com/redhat/thermostat/vm/heap/analysis/client/swing/internal/HeapDetailsSwingTest.java vm-heap-analysis/client-swing/src/test/java/com/redhat/thermostat/vm/heap/analysis/client/swing/internal/SwingHeapTreeMapViewTest.java
diffstat 9 files changed, 329 insertions(+), 97 deletions(-) [+]
line wrap: on
line diff
--- a/vm-heap-analysis/client-core/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/core/HeapDumpDetailsView.java	Wed Jun 29 12:23:09 2016 -0400
+++ b/vm-heap-analysis/client-core/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/core/HeapDumpDetailsView.java	Wed Jun 29 12:23:10 2016 -0400
@@ -46,5 +46,8 @@
     public abstract void addSubView(LocalizedString title, ObjectDetailsView child);
     public abstract void addSubView(LocalizedString title, HeapTreeMapView child);
     public abstract void removeSubView(LocalizedString title);
+    public abstract void updateView(final HeapHistogramView histogramView,
+                                    final ObjectDetailsView detailsView,
+                                    final HeapTreeMapView treeMapView);
 }
 
--- a/vm-heap-analysis/client-core/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/core/internal/HeapDumpController.java	Wed Jun 29 12:23:09 2016 -0400
+++ b/vm-heap-analysis/client-core/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/core/internal/HeapDumpController.java	Wed Jun 29 12:23:10 2016 -0400
@@ -107,6 +107,7 @@
     private ObjectDetailsViewProvider objectDetailsViewProvider;
     private ObjectRootsViewProvider objectRootsViewProvider;
     private HeapDumpListViewProvider heapDumpListViewProvider;
+    private HeapDumpDetailsController heapDumpDetailsController;
 
     private ProgressNotifier notifier;
     
@@ -155,6 +156,14 @@
         this.vmDao = vmMemoryStatDao;
         this.heapDAO = heapDao;
         this.heapDumpListViewProvider = heapDumpListViewProvider;
+
+        this.heapDumpDetailsController = new HeapDumpDetailsController(
+                        appService,
+                        detailsViewProvider,
+                        histogramViewProvider,
+                        treeMapViewProvider,
+                        objectDetailsViewProvider,
+                        objectRootsViewProvider);
         
         model = new OverviewChart(
                     null,
@@ -372,15 +381,9 @@
     }
 
     private void showHeapDumpDetails(HeapDump dump) {
-        HeapDumpDetailsController controller =
-                new HeapDumpDetailsController(appService, detailsViewProvider,
-                                              histogramViewProvider,
-                                              treeMapViewProvider,
-                                              objectDetailsViewProvider,
-                                              objectRootsViewProvider);
-        controller.setDump(dump);
+        heapDumpDetailsController.setDump(dump);
         view.setActiveDump(dump);
-        view.setChildView(controller.getView());
+        view.setChildView(heapDumpDetailsController.getView());
         view.openDumpView();
     }
 
--- a/vm-heap-analysis/client-core/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/core/internal/HeapDumpDetailsController.java	Wed Jun 29 12:23:09 2016 -0400
+++ b/vm-heap-analysis/client-core/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/core/internal/HeapDumpDetailsController.java	Wed Jun 29 12:23:10 2016 -0400
@@ -38,6 +38,7 @@
 
 import java.io.IOException;
 import java.util.Collection;
+import java.util.Objects;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
@@ -47,8 +48,6 @@
 import com.redhat.thermostat.common.ApplicationService;
 import com.redhat.thermostat.common.NotImplementedException;
 import com.redhat.thermostat.common.utils.LoggingUtils;
-import com.redhat.thermostat.shared.locale.LocalizedString;
-import com.redhat.thermostat.shared.locale.Translate;
 import com.redhat.thermostat.vm.heap.analysis.client.core.HeapDumpDetailsView;
 import com.redhat.thermostat.vm.heap.analysis.client.core.HeapDumpDetailsViewProvider;
 import com.redhat.thermostat.vm.heap.analysis.client.core.HeapHistogramView;
@@ -59,23 +58,20 @@
 import com.redhat.thermostat.vm.heap.analysis.client.core.ObjectDetailsView;
 import com.redhat.thermostat.vm.heap.analysis.client.core.ObjectDetailsViewProvider;
 import com.redhat.thermostat.vm.heap.analysis.client.core.ObjectRootsViewProvider;
-import com.redhat.thermostat.vm.heap.analysis.client.locale.LocaleResources;
 import com.redhat.thermostat.vm.heap.analysis.common.HeapDump;
 import com.redhat.thermostat.vm.heap.analysis.common.ObjectHistogram;
 import com.sun.tools.hat.internal.model.JavaHeapObject;
 
 public class HeapDumpDetailsController {
 
-    private static final Translate<LocaleResources> translator = LocaleResources.createLocalizer();
-
     private static final Logger log = LoggingUtils.getLogger(HeapDumpDetailsController.class);
 
     private final ApplicationService appService;
 
     private HeapDumpDetailsView view;
     private HeapDump heapDump;
+    private HeapTreeMapView heapTreeMapView;
     private HeapHistogramViewProvider histogramViewProvider;
-    private HeapTreeMapViewProvider treeMapViewProvider;
     private ObjectDetailsViewProvider objectDetailsViewProvider;
     private ObjectRootsViewProvider objectRootsViewProvider;
     private HeapHistogramView heapHistogramView;
@@ -83,48 +79,50 @@
     public HeapDumpDetailsController(ApplicationService appService, HeapDumpDetailsViewProvider viewProvider, HeapHistogramViewProvider histogramProvider, HeapTreeMapViewProvider treeMapProvider, ObjectDetailsViewProvider objectDetailsProvider, ObjectRootsViewProvider objectRootsProvider) {
         this.appService = appService;
         this.histogramViewProvider = histogramProvider;
-        this.treeMapViewProvider = treeMapProvider;
         this.objectDetailsViewProvider = objectDetailsProvider;
         this.objectRootsViewProvider = objectRootsProvider;
         view = viewProvider.createView();
+        heapTreeMapView = treeMapProvider.createView();
     }
 
     public void setDump(HeapDump dump) {
         this.heapDump = dump;
+        ObjectHistogram histogram = null;
+
         try {
-            ObjectHistogram histogram = heapDump.getHistogram();
-            heapHistogramView = histogramViewProvider.createView();
-            heapHistogramView.setHistogram(histogram);
-            heapHistogramView.addHistogramActionListener(new ActionListener<HistogramAction>() {
-                @Override
-                public void actionPerformed(ActionEvent<HistogramAction> actionEvent) {
-                    switch (actionEvent.getActionId()) {
-                        case SEARCH:
-                            searchForObject((String) actionEvent.getPayload());
-                            break;
-                        default:
-                            throw new NotImplementedException("unknown action fired by " + actionEvent.getSource());
-                    }
-                }
-            });
-
-            LocalizedString title = translator.localize(LocaleResources.HEAP_DUMP_SECTION_HISTOGRAM);
-            view.addSubView(title, heapHistogramView);
-            
-            HeapTreeMapView heapTreeMapView = treeMapViewProvider.createView();
-            heapTreeMapView.display(heapDump.getHistogram());
-            LocalizedString titleTreeMap = translator.localize(LocaleResources.HEAP_DUMP_SECTION_TREEMAP);
-            view.addSubView(titleTreeMap, heapTreeMapView);
+            histogram = dump.getHistogram();
         } catch (IOException e) {
             log.log(Level.SEVERE, "unexpected error while reading heap dump", e);
         }
 
-        ObjectDetailsController controller = new ObjectDetailsController(appService, dump, objectDetailsViewProvider, objectRootsViewProvider);
+        Objects.requireNonNull(histogram);
+
+        heapHistogramView = histogramViewProvider.createView();
+        heapHistogramView.setHistogram(histogram);
+        heapHistogramView.addHistogramActionListener(new ActionListener<HistogramAction>() {
+            @Override
+            public void actionPerformed(ActionEvent<HistogramAction> actionEvent) {
+                switch (actionEvent.getActionId()) {
+                    case SEARCH:
+                        searchForObject((String) actionEvent.getPayload());
+                        break;
+                    default:
+                        throw new NotImplementedException(
+                                "unknown action fired by " + actionEvent.getSource());
+                }
+            }
+        });
+
+        heapTreeMapView.display(histogram);
+
+        ObjectDetailsController controller = new ObjectDetailsController(appService, dump,
+                objectDetailsViewProvider, objectRootsViewProvider);
         ObjectDetailsView detailsView = controller.getView();
-        view.addSubView(translator.localize(LocaleResources.HEAP_DUMP_SECTION_OBJECT_BROWSER), detailsView);
+
+        view.updateView(heapHistogramView, detailsView, heapTreeMapView);
 
         // do a dummy search right now to prep the index
-        heapDump.searchObjects("A_RANDOM_PATTERN", 1);
+        dump.searchObjects("A_RANDOM_PATTERN", 1);
     }
 
     private void searchForObject(final String searchText) {
--- a/vm-heap-analysis/client-core/src/test/java/com/redhat/thermostat/vm/heap/analysis/client/core/internal/HeapDumpControllerTest.java	Wed Jun 29 12:23:09 2016 -0400
+++ b/vm-heap-analysis/client-core/src/test/java/com/redhat/thermostat/vm/heap/analysis/client/core/internal/HeapDumpControllerTest.java	Wed Jun 29 12:23:10 2016 -0400
@@ -49,6 +49,7 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
@@ -96,6 +97,7 @@
 import com.redhat.thermostat.vm.heap.analysis.common.DumpFile;
 import com.redhat.thermostat.vm.heap.analysis.common.HeapDAO;
 import com.redhat.thermostat.vm.heap.analysis.common.HeapDump;
+import com.redhat.thermostat.vm.heap.analysis.common.ObjectHistogram;
 import com.redhat.thermostat.vm.heap.analysis.common.model.HeapInfo;
 import com.redhat.thermostat.vm.memory.common.VmMemoryStatDAO;
 import com.redhat.thermostat.vm.memory.common.model.VmMemoryStat;
@@ -286,10 +288,13 @@
     }
     
     @Test
-    public void testOpenDumpCalledWhenPreviousDump() {
+    public void testOpenDumpCalledWhenPreviousDump() throws IOException {
 
         setUpTimers();
         HeapDump dump = mock(HeapDump.class);
+
+        ObjectHistogram histogram = mock(ObjectHistogram.class);
+        when(dump.getHistogram()).thenReturn(histogram);
         
         HeapInfo info1 = mock(HeapInfo.class);
         when(dump.getInfo()).thenReturn(info1);
--- a/vm-heap-analysis/client-core/src/test/java/com/redhat/thermostat/vm/heap/analysis/client/core/internal/HeapDumpDetailsControllerTest.java	Wed Jun 29 12:23:09 2016 -0400
+++ b/vm-heap-analysis/client-core/src/test/java/com/redhat/thermostat/vm/heap/analysis/client/core/internal/HeapDumpDetailsControllerTest.java	Wed Jun 29 12:23:10 2016 -0400
@@ -36,6 +36,9 @@
 
 package com.redhat.thermostat.vm.heap.analysis.client.core.internal;
 
+import junit.framework.Assert;
+
+import static junit.framework.Assert.assertTrue;
 import static org.mockito.Matchers.anyInt;
 import static org.mockito.Matchers.isA;
 import static org.mockito.Mockito.mock;
@@ -49,7 +52,6 @@
 import org.junit.Test;
 
 import com.redhat.thermostat.common.ApplicationService;
-import com.redhat.thermostat.shared.locale.LocalizedString;
 import com.redhat.thermostat.vm.heap.analysis.client.core.HeapDumpDetailsView;
 import com.redhat.thermostat.vm.heap.analysis.client.core.HeapDumpDetailsViewProvider;
 import com.redhat.thermostat.vm.heap.analysis.client.core.HeapHistogramView;
@@ -104,24 +106,45 @@
     }
 
     @Test
-    public void verifyInitialize() throws IOException {
-        ApplicationService appService = mock(ApplicationService.class);
+    public void testSetDumpFailsWithEmptyDump() throws IOException {
+        HeapDumpDetailsController controller = setupController();
+
+        HeapDump emptyDump = mock(HeapDump.class);
+        when(emptyDump.getHistogram()).thenReturn(null);
 
-        ObjectHistogram histogram = mock(ObjectHistogram.class);
+        boolean caught = false;
+        try {
+            controller.setDump(emptyDump);
+        } catch (NullPointerException e) {
+            caught = true;
+        }
+        assertTrue("Null pointer exception expected", caught);
+    }
+
+    @Test
+    public void testSetDumpWorksWithValidDump() throws IOException {
+        HeapDumpDetailsController controller = setupController();
 
         HeapDump dump = mock(HeapDump.class);
+        ObjectHistogram histogram = mock(ObjectHistogram.class);
         when(dump.getHistogram()).thenReturn(histogram);
 
-        HeapDumpDetailsController controller = new HeapDumpDetailsController(
+        try {
+            controller.setDump(dump);
+        } catch (NullPointerException e) {
+            Assert.fail("Did not expect null pointer exception");
+        }
+
+        verify(dump).searchObjects(isA(String.class), anyInt());
+        verify(view).updateView(isA(HeapHistogramView.class), isA(ObjectDetailsView.class),
+                isA(HeapTreeMapView.class));
+    }
+
+    private HeapDumpDetailsController setupController() {
+        ApplicationService appService = mock(ApplicationService.class);
+        return new HeapDumpDetailsController(
                 appService, viewProvider, histogramProvider, treeMapProvider,
                 objectDetailsProvider, objectRootsProvider);
-        controller.setDump(dump);
-
-        verify(dump).searchObjects(isA(String.class), anyInt());
-        verify(view)
-                .addSubView(isA(LocalizedString.class), isA(HeapHistogramView.class));
-        verify(view)
-                .addSubView(isA(LocalizedString.class), isA(ObjectDetailsView.class));
     }
 
 }
--- a/vm-heap-analysis/client-swing/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/swing/internal/HeapDetailsSwing.java	Wed Jun 29 12:23:09 2016 -0400
+++ b/vm-heap-analysis/client-swing/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/swing/internal/HeapDetailsSwing.java	Wed Jun 29 12:23:10 2016 -0400
@@ -45,13 +45,17 @@
 import com.redhat.thermostat.client.swing.SwingComponent;
 import com.redhat.thermostat.client.swing.components.ThermostatTabbedPane;
 import com.redhat.thermostat.shared.locale.LocalizedString;
+import com.redhat.thermostat.shared.locale.Translate;
 import com.redhat.thermostat.vm.heap.analysis.client.core.HeapDumpDetailsView;
 import com.redhat.thermostat.vm.heap.analysis.client.core.HeapHistogramView;
 import com.redhat.thermostat.vm.heap.analysis.client.core.HeapTreeMapView;
 import com.redhat.thermostat.vm.heap.analysis.client.core.ObjectDetailsView;
+import com.redhat.thermostat.vm.heap.analysis.client.locale.LocaleResources;
 
 public class HeapDetailsSwing extends HeapDumpDetailsView implements SwingComponent {
 
+    private static final Translate<LocaleResources> translator = LocaleResources.createLocalizer();
+
     /** For TESTING only! */
     static final String TAB_NAME = "tabs";
 
@@ -65,8 +69,17 @@
         visiblePane.add(tabPane, BorderLayout.CENTER);
 
         tabPane.setName(TAB_NAME);
+        tabPane.addTab(
+                translator.localize(LocaleResources.HEAP_DUMP_SECTION_TREEMAP).getContents(), null);
+        tabPane.addTab(
+                translator.localize(LocaleResources.HEAP_DUMP_SECTION_OBJECT_BROWSER).getContents(), null);
+        tabPane.addTab(
+                translator.localize(LocaleResources.HEAP_DUMP_SECTION_HISTOGRAM).getContents(), null);
+        tabPane.setSelectedIndex(0);
     }
 
+
+
     @Override
     public void addSubView(final LocalizedString title, final HeapHistogramView view) {
         verifyIsSwingComponent(view);
@@ -128,4 +141,25 @@
         });
     }
 
+    @Override
+    public void updateView(final HeapHistogramView histogramView,
+                           final ObjectDetailsView detailsView,
+                           final HeapTreeMapView treeMapView) {
+
+        verifyIsSwingComponent(histogramView);
+        verifyIsSwingComponent(detailsView);
+        verifyIsSwingComponent(treeMapView);
+
+        SwingUtilities.invokeLater(new Runnable() {
+            @Override
+            public void run() {
+                tabPane.setComponentAt(0, ((SwingComponent) treeMapView).getUiComponent());
+                tabPane.setComponentAt(1, ((SwingComponent) detailsView).getUiComponent());
+                tabPane.setComponentAt(2, ((SwingComponent) histogramView).getUiComponent());
+                tabPane.revalidate();
+                tabPane.repaint();
+            }
+        });
+    }
+
 }
--- a/vm-heap-analysis/client-swing/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/swing/internal/SwingHeapTreeMapView.java	Wed Jun 29 12:23:09 2016 -0400
+++ b/vm-heap-analysis/client-swing/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/swing/internal/SwingHeapTreeMapView.java	Wed Jun 29 12:23:10 2016 -0400
@@ -40,6 +40,7 @@
 import java.awt.Component;
 
 import javax.swing.JPanel;
+import javax.swing.SwingUtilities;
 
 import com.redhat.thermostat.client.swing.SwingComponent;
 import com.redhat.thermostat.client.swing.components.experimental.TreeMapComponent;
@@ -52,8 +53,8 @@
 public class SwingHeapTreeMapView extends HeapTreeMapView implements SwingComponent {
     
     private TreeMapComponent treeMap;
-    private final JPanel panel;
-    
+    private JPanel panel;
+
     public SwingHeapTreeMapView() {
         treeMap = new TreeMapComponent();
         treeMap.setToolTipRenderer(new WeightAsSizeRenderer());
@@ -63,11 +64,19 @@
     }
 
     @Override
-    public void display(ObjectHistogram histogram) {
-        TreeMapNode model = HistogramConverter.convertToTreeMap(histogram);
-        treeMap.setModel(model);
-        panel.add(treeMap, BorderLayout.CENTER);
-        panel.add(new TreeMapToolbar(treeMap), BorderLayout.NORTH);
+    public void display(final ObjectHistogram histogram) {
+        SwingUtilities.invokeLater(new Runnable() {
+            @Override
+            public void run() {
+                TreeMapNode model = HistogramConverter.convertToTreeMap(histogram);
+                treeMap.setModel(model);
+                panel.removeAll();
+                panel.add(treeMap, BorderLayout.CENTER);
+                panel.add(new TreeMapToolbar(treeMap), BorderLayout.NORTH);
+                panel.revalidate();
+                panel.repaint();
+            }
+        });
     }
 
     @Override
--- a/vm-heap-analysis/client-swing/src/test/java/com/redhat/thermostat/vm/heap/analysis/client/swing/internal/HeapDetailsSwingTest.java	Wed Jun 29 12:23:09 2016 -0400
+++ b/vm-heap-analysis/client-swing/src/test/java/com/redhat/thermostat/vm/heap/analysis/client/swing/internal/HeapDetailsSwingTest.java	Wed Jun 29 12:23:10 2016 -0400
@@ -36,17 +36,23 @@
 
 package com.redhat.thermostat.vm.heap.analysis.client.swing.internal;
 
+import junit.framework.Assert;
+import net.java.openjdk.cacio.ctc.junit.CacioFESTRunner;
+
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
 import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.concurrent.Callable;
 
 import javax.swing.JFrame;
 import javax.swing.JPanel;
-
-import net.java.openjdk.cacio.ctc.junit.CacioFESTRunner;
+import javax.swing.SwingUtilities;
 
 import org.fest.swing.annotation.GUITest;
 import org.fest.swing.edt.FailOnThreadViolationRepaintManager;
@@ -54,6 +60,7 @@
 import org.fest.swing.edt.GuiTask;
 import org.fest.swing.fixture.FrameFixture;
 import org.fest.swing.fixture.JTabbedPaneFixture;
+import org.fest.swing.util.Triple;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.BeforeClass;
@@ -64,13 +71,21 @@
 import com.redhat.thermostat.annotations.internal.CacioTest;
 import com.redhat.thermostat.client.swing.EdtHelper;
 import com.redhat.thermostat.shared.locale.LocalizedString;
+import com.redhat.thermostat.shared.locale.Translate;
+import com.redhat.thermostat.vm.heap.analysis.client.core.HeapHistogramView;
+import com.redhat.thermostat.vm.heap.analysis.client.core.HeapTreeMapView;
+import com.redhat.thermostat.vm.heap.analysis.client.core.ObjectDetailsView;
+import com.redhat.thermostat.vm.heap.analysis.client.locale.LocaleResources;
 
 @Category(CacioTest.class)
 @RunWith(CacioFESTRunner.class)
 public class HeapDetailsSwingTest {
 
+    private static final Translate<LocaleResources> translator = LocaleResources.createLocalizer();
+
     private JFrame frame;
     private FrameFixture frameFixture;
+    private JTabbedPaneFixture tabPane;
     private HeapDetailsSwing view;
 
     @BeforeClass
@@ -100,57 +115,192 @@
 
     @GUITest
     @Test
-    public void verifyTabsAdded() throws InvocationTargetException, InterruptedException {
+    public void testTabSetup() throws InvocationTargetException, InterruptedException {
+        frameFixture.show();
+
+        SwingUtilities.invokeAndWait(new Runnable() {
+            @Override
+            public void run() {
+                tabPane = frameFixture.tabbedPane("tabs");
+                assertNotNull(tabPane);
+
+                ArrayList<String> tabTitles = new ArrayList<>();
+                tabTitles.addAll(Arrays.asList(tabPane.tabTitles()));
+
+                assertEquals(0, tabPane.component().getSelectedIndex());
+                assertEquals(3, tabTitles.size());
+                assertTrue(tabTitles.contains(
+                        translator.localize(LocaleResources.HEAP_DUMP_SECTION_TREEMAP).getContents()));
+                assertTrue(tabTitles.contains(
+                        translator.localize(LocaleResources.HEAP_DUMP_SECTION_OBJECT_BROWSER).getContents()));
+                assertTrue(tabTitles.contains(
+                        translator.localize(LocaleResources.HEAP_DUMP_SECTION_HISTOGRAM).getContents()));
+            }
+        });
+    }
+
+    @GUITest
+    @Test
+    public void testUpdateView() throws InvocationTargetException, InterruptedException {
         frameFixture.show();
 
-        JTabbedPaneFixture tabPane = frameFixture.tabbedPane("tabs");
-        assertNotNull(tabPane);
+        SwingUtilities.invokeAndWait(new Runnable() {
+            @Override
+            public void run() {
+                tabPane = frameFixture.tabbedPane("tabs");
+                assertNotNull(tabPane);
+                assertEquals(0, tabPane.component().getComponents().length);
+            }
+        });
+
+        Triple<HistogramPanel, ObjectDetailsPanel, SwingHeapTreeMapView> setup = setupViews();
+
+        String exceptionMessage = "";
+        try {
+            view.updateView(null, setup.ii, setup.iii);
+        } catch (IllegalArgumentException e) {
+            exceptionMessage = e.getMessage();
+        }
+        assertEquals("component is not swing", exceptionMessage);
+
+        exceptionMessage = "";
+        try {
+            view.updateView(setup.i, null, setup.iii);
+        } catch (IllegalArgumentException e) {
+            exceptionMessage = e.getMessage();
+        }
+        assertEquals("component is not swing", exceptionMessage);
 
-        HistogramPanel histogramView = mock(HistogramPanel.class);
-        when(histogramView.getUiComponent()).thenReturn(new EdtHelper().callAndWait(new Callable<JPanel>() {
+        exceptionMessage = "";
+        try {
+            view.updateView(setup.i, setup.ii, null);
+        } catch (IllegalArgumentException e) {
+            exceptionMessage = e.getMessage();
+        }
+        assertEquals("component is not swing", exceptionMessage);
+
+        try {
+            view.updateView(setup.i, setup.ii, setup.iii);
+        } catch (IllegalArgumentException e) {
+            Assert.fail("no illegal argument exception expected");
+        }
+
+        SwingUtilities.invokeAndWait(new Runnable() {
             @Override
-            public JPanel call() throws Exception {
-                return new JPanel();
+            public void run() {
+                assertEquals(3, tabPane.component().getComponents().length);
             }
-        }));
+        });
+    }
+
+    @GUITest
+    @Test
+    public void testAddRemove() throws InvocationTargetException, InterruptedException {
+        frameFixture.show();
 
-        ObjectDetailsPanel objectDetailsView = mock(ObjectDetailsPanel.class);
-        when(objectDetailsView.getUiComponent()).thenReturn(new EdtHelper().callAndWait(new Callable<JPanel>() {
+        final ArrayList<String> tabNames = new ArrayList<>();
+        SwingUtilities.invokeAndWait(new Runnable() {
             @Override
-            public JPanel call() throws Exception {
-                return new JPanel();
+            public void run() {
+                tabPane = frameFixture.tabbedPane("tabs");
+                assertNotNull(tabPane);
+                tabNames.addAll(Arrays.asList(tabPane.tabTitles()));
+            }
+        });
+
+        for (String tabName : tabNames) {
+            view.removeSubView(new LocalizedString(tabName));
+        }
+
+        SwingUtilities.invokeAndWait(new Runnable() {
+            @Override
+            public void run() {
+                assertEquals(0, tabPane.tabTitles().length);
             }
-        }));
-        view.addSubView(new LocalizedString("test1"), histogramView);
-        view.addSubView(new LocalizedString("test2"), objectDetailsView);
+        });
+
+        String exceptionMessage = "";
+        try {
+            view.addSubView(new LocalizedString("test1"), (HeapHistogramView) null);
+        } catch (IllegalArgumentException e) {
+            exceptionMessage = e.getMessage();
+        }
+        assertEquals("component is not swing", exceptionMessage);
+
+        exceptionMessage = "";
+        try {
+            view.addSubView(new LocalizedString("test2"), (ObjectDetailsView) null);
+        } catch (IllegalArgumentException e) {
+            exceptionMessage = e.getMessage();
+        }
+        assertEquals("component is not swing", exceptionMessage);
 
-        tabPane.requireTabTitles("test1", "test2");
+        exceptionMessage = "";
+        try {
+            view.addSubView(new LocalizedString("test3"), (HeapTreeMapView) null);
+        } catch (IllegalArgumentException e) {
+            exceptionMessage = e.getMessage();
+        }
+        assertEquals("component is not swing", exceptionMessage);
+
+        Triple<HistogramPanel, ObjectDetailsPanel, SwingHeapTreeMapView> setup = setupViews();
+        try {
+            view.addSubView(new LocalizedString("test1"), setup.i);
+            view.addSubView(new LocalizedString("test2"), setup.ii);
+            view.addSubView(new LocalizedString("test3"), setup.iii);
+        } catch (IllegalArgumentException e) {
+            Assert.fail("no illegal argument exception expected");
+        }
+
+        SwingUtilities.invokeAndWait(new Runnable() {
+            @Override
+            public void run() {
+                ArrayList<String> tabTitles = new ArrayList<>();
+                tabTitles.addAll(Arrays.asList(tabPane.tabTitles()));
+
+                assertEquals(3, tabTitles.size());
+                assertTrue(tabTitles.contains("test1"));
+                assertTrue(tabTitles.contains("test2"));
+                assertTrue(tabTitles.contains("test3"));
+            }
+        });
     }
 
 
-    @GUITest
-    @Test
-    public void verifyAddRemove() throws InvocationTargetException, InterruptedException {
-        frameFixture.show();
-
-        JTabbedPaneFixture tabPane = frameFixture.tabbedPane("tabs");
-        assertNotNull(tabPane);
-
+    private Triple<HistogramPanel, ObjectDetailsPanel, SwingHeapTreeMapView> setupViews() {
         HistogramPanel histogramView = mock(HistogramPanel.class);
-        when(histogramView.getUiComponent()).thenReturn(new EdtHelper().callAndWait(new Callable<JPanel>() {
-            @Override
-            public JPanel call() throws Exception {
-                return new JPanel();
-            }
-        }));
+        ObjectDetailsPanel objectDetailsView = mock(ObjectDetailsPanel.class);
+        SwingHeapTreeMapView treeMapView = mock(SwingHeapTreeMapView.class);
+
+        try {
+            when(histogramView.getUiComponent()).thenReturn(new EdtHelper().callAndWait(new Callable<JPanel>() {
+                @Override
+                public JPanel call() throws Exception {
+                    return new JPanel();
+                }
+            }));
+
 
-        view.addSubView(new LocalizedString("test1"), histogramView);
+            when(objectDetailsView.getUiComponent()).thenReturn(new EdtHelper().callAndWait(new Callable<JPanel>() {
+                @Override
+                public JPanel call() throws Exception {
+                    return new JPanel();
+                }
+            }));
 
-        tabPane.requireTabTitles("test1");
 
-        view.removeSubView(new LocalizedString("test1"));
+            when(treeMapView.getUiComponent()).thenReturn(new EdtHelper().callAndWait(new Callable<JPanel>() {
+                @Override
+                public JPanel call() throws Exception {
+                    return new JPanel();
+                }
+            }));
+        } catch (InvocationTargetException | InterruptedException e) {
+            Assert.fail("Did not expect exception");
+        }
 
-        tabPane.requireTabTitles();
+        return new Triple<>(histogramView, objectDetailsView, treeMapView);
     }
+
 }
 
--- a/vm-heap-analysis/client-swing/src/test/java/com/redhat/thermostat/vm/heap/analysis/client/swing/internal/SwingHeapTreeMapViewTest.java	Wed Jun 29 12:23:09 2016 -0400
+++ b/vm-heap-analysis/client-swing/src/test/java/com/redhat/thermostat/vm/heap/analysis/client/swing/internal/SwingHeapTreeMapViewTest.java	Wed Jun 29 12:23:10 2016 -0400
@@ -38,7 +38,9 @@
 
 import static org.junit.Assert.assertEquals;
 
+import org.fest.swing.edt.FailOnThreadViolationRepaintManager;
 import org.junit.Before;
+import org.junit.BeforeClass;
 import org.junit.Test;
 
 import com.redhat.thermostat.client.swing.components.experimental.TreeMapNode;
@@ -51,6 +53,11 @@
     private SwingHeapTreeMapView.WeightAsSizeRenderer renderer;
     private TreeMapNode root;
 
+    @BeforeClass
+    public static void setUpOnce() {
+        FailOnThreadViolationRepaintManager.install();
+    }
+
     @Before
     public void setUp() {
         root = new TreeMapNode(SOME_ROOT, WEIGHT);