Mercurial > hg > release > thermostat-0.4
changeset 336:a43132536a6a
Allow plugins to add menu items
Reviewed-by: rkennke, vanaltj
Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2012-May/001571.html
line wrap: on
line diff
--- a/client/core/src/main/java/com/redhat/thermostat/client/MainView.java Tue May 29 11:47:38 2012 -0400 +++ b/client/core/src/main/java/com/redhat/thermostat/client/MainView.java Wed May 30 12:29:39 2012 -0400 @@ -38,6 +38,7 @@ import java.awt.Component; +import com.redhat.thermostat.client.osgi.service.MenuAction; import com.redhat.thermostat.client.osgi.service.VMContextAction; import com.redhat.thermostat.common.ActionListener; import com.redhat.thermostat.common.dao.Ref; @@ -73,5 +74,9 @@ void setSubView(Component view); + void addMenu(String parentMenuName, MenuAction action); + + void removeMenu(String parentMenuName, MenuAction action); + void registerVMContextAction(VMContextAction action); }
--- a/client/core/src/main/java/com/redhat/thermostat/client/MainWindowControllerImpl.java Tue May 29 11:47:38 2012 -0400 +++ b/client/core/src/main/java/com/redhat/thermostat/client/MainWindowControllerImpl.java Wed May 30 12:29:39 2012 -0400 @@ -41,8 +41,7 @@ import java.util.logging.Level; import java.util.logging.Logger; -import org.osgi.framework.BundleException; - +import com.redhat.thermostat.client.osgi.service.MenuAction; import com.redhat.thermostat.client.osgi.service.VMContextAction; import com.redhat.thermostat.client.ui.AboutDialog; import com.redhat.thermostat.client.ui.AgentConfigurationController; @@ -83,13 +82,27 @@ private ApplicationInfo appInfo; private UiFacadeFactory facadeFactory; + private MenuRegistry menuRegistry; + private MenuRegistry.MenuListener menuListener = new MenuRegistry.MenuListener() { + + @Override + public void removed(String parentMenuName, MenuAction action) { + view.removeMenu(parentMenuName, action); + } + + @Override + public void added(String parentMenuName, MenuAction action) { + view.addMenu(parentMenuName, action); + } + }; private boolean showHistory; private VmInformationControllerProvider vmInfoControllerProvider; - - public MainWindowControllerImpl(UiFacadeFactory facadeFactory, MainView view) { + + public MainWindowControllerImpl(UiFacadeFactory facadeFactory, MainView view, MenuRegistry menuRegistry) { this.facadeFactory = facadeFactory; + this.menuRegistry = menuRegistry; ApplicationContext ctx = ApplicationContext.getInstance(); DAOFactory daoFactory = ctx.getDAOFactory(); @@ -110,6 +123,9 @@ } updateView(); + + menuRegistry.start(); + menuRegistry.addMenuListener(menuListener); } private class HostsVMsLoaderImpl implements HostsVMsLoader { @@ -212,6 +228,10 @@ } private void shutdownApplication() { + menuRegistry.removeMenuListener(menuListener); + menuListener = null; + menuRegistry.stop(); + view.hideMainWindow(); ApplicationContext.getInstance().getTimerFactory().shutdown(); shutdownOSGiFramework();
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/client/core/src/main/java/com/redhat/thermostat/client/MenuRegistry.java Wed May 30 12:29:39 2012 -0400 @@ -0,0 +1,142 @@ +/* + * 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.client; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.concurrent.CopyOnWriteArrayList; + +import org.osgi.framework.BundleContext; +import org.osgi.framework.Constants; +import org.osgi.framework.FrameworkUtil; +import org.osgi.framework.InvalidSyntaxException; +import org.osgi.framework.ServiceReference; +import org.osgi.util.tracker.ServiceTracker; + +import com.redhat.thermostat.client.osgi.service.MenuAction; + +public class MenuRegistry { + + public static interface MenuListener { + + public void added(String parentMenuName, MenuAction action); + + public void removed(String parentMenuName, MenuAction action); + } + + public static final String PARENT_MENU = "parentMenu"; + + private static final String FILTER = "(&(" + Constants.OBJECTCLASS + "=" + MenuAction.class.getName() + ")(" + PARENT_MENU + "=*))"; + + private ServiceTracker menuTracker; + + private Map<String,List<MenuAction>> menus = new HashMap<>(); + private List<MenuListener> listeners = new CopyOnWriteArrayList<>(); + + public MenuRegistry(BundleContext context) throws InvalidSyntaxException { + menuTracker = new ServiceTracker(context, FrameworkUtil.createFilter(FILTER), null) { + @Override + public Object addingService(ServiceReference reference) { + MenuAction action = (MenuAction) super.addingService(reference); + String parentMenuName = (String) reference.getProperty(PARENT_MENU); + menuAdded(parentMenuName, action); + return action; + } + + @Override + public void removedService(ServiceReference reference, Object service) { + if (!(service instanceof MenuAction)) { + throw new AssertionError("removing a non-MenuAction service"); + } + String parentMenuName = (String) reference.getProperty(PARENT_MENU); + menuRemoved(parentMenuName, (MenuAction)service); + super.removedService(reference, service); + } + }; + } + + public void start() { + menuTracker.open(); + } + + public void stop() { + menuTracker.close(); + } + + public void addMenuListener(MenuListener listener) { + listeners.add(listener); + + for (Entry<String,List<MenuAction>> entry: menus.entrySet()) { + for (MenuAction action: entry.getValue()) { + listener.added(entry.getKey(), action); + } + } + } + + public void removeMenuListener(MenuListener listener) { + listeners.remove(listener); + } + + private void menuAdded(String parentMenuName, MenuAction action) { + if (!menus.containsKey(parentMenuName)) { + menus.put(parentMenuName, new ArrayList<MenuAction>()); + } + List<MenuAction> list = menus.get(parentMenuName); + list.add(action); + for (MenuListener listener: listeners) { + listener.added(parentMenuName, action); + } + } + + private void menuRemoved(String parentMenuName, MenuAction action) { + if (!menus.containsKey(parentMenuName)) { + throw new IllegalArgumentException("unknown parent menu name"); + } + List<MenuAction> list = menus.get(parentMenuName); + if (!list.contains(action)) { + throw new IllegalArgumentException("unknown menu action"); + } + + list.remove(action); + for (MenuListener listener: listeners) { + listener.removed(parentMenuName, action); + } + } + +}
--- a/client/core/src/main/java/com/redhat/thermostat/client/UiFacadeFactoryImpl.java Tue May 29 11:47:38 2012 -0400 +++ b/client/core/src/main/java/com/redhat/thermostat/client/UiFacadeFactoryImpl.java Wed May 30 12:29:39 2012 -0400 @@ -40,8 +40,6 @@ import java.util.Collection; import java.util.concurrent.CountDownLatch; -import org.osgi.framework.BundleContext; - import com.redhat.thermostat.client.osgi.service.VMContextAction; import com.redhat.thermostat.client.osgi.service.VmInformationService; import com.redhat.thermostat.client.ui.HostInformationController; @@ -57,11 +55,17 @@ private Collection<VmInformationService> vmInformationServices = new ArrayList<>(); private Collection<VMContextAction> contextAction = new ArrayList<>(); - + + private MenuRegistry menuRegistry; + + public UiFacadeFactoryImpl(MenuRegistry registry) { + menuRegistry = registry; + } + @Override public MainWindowController getMainWindow() { MainView mainView = new MainWindow(); - return new MainWindowControllerImpl(this, mainView); + return new MainWindowControllerImpl(this, mainView, menuRegistry); } @Override
--- a/client/core/src/main/java/com/redhat/thermostat/client/osgi/ThermostatActivator.java Tue May 29 11:47:38 2012 -0400 +++ b/client/core/src/main/java/com/redhat/thermostat/client/osgi/ThermostatActivator.java Wed May 30 12:29:39 2012 -0400 @@ -43,6 +43,7 @@ import com.redhat.thermostat.client.GUIClientCommand; import com.redhat.thermostat.client.Main; +import com.redhat.thermostat.client.MenuRegistry; import com.redhat.thermostat.client.UiFacadeFactory; import com.redhat.thermostat.client.UiFacadeFactoryImpl; import com.redhat.thermostat.common.cli.CommandRegistry; @@ -57,7 +58,8 @@ @Override public void start(final BundleContext context) throws Exception { - UiFacadeFactory uiFacadeFactory = new UiFacadeFactoryImpl(); + MenuRegistry menuRegistry = new MenuRegistry(context); + UiFacadeFactory uiFacadeFactory = new UiFacadeFactoryImpl(menuRegistry); vmInfoServiceTracker = new VmInformationServiceTracker(context, uiFacadeFactory); vmInfoServiceTracker.open();
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/client/core/src/main/java/com/redhat/thermostat/client/osgi/service/MenuAction.java Wed May 30 12:29:39 2012 -0400 @@ -0,0 +1,55 @@ +/* + * 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.client.osgi.service; + +/** + * Allows plugins to register menu items. + * <p> + * To register a menu item for for the menu "File" in thermostat client window, + * register a service that implements this class with the property + * "parentMenu" set to "File". + */ +public interface MenuAction { + + /** The string displayed as the menu item name */ + String getName(); + + /** A generic description of the menu item */ + String getDescription(); + + /** Invoked when the user selects this menu item */ + void execute(); +}
--- a/client/core/src/main/java/com/redhat/thermostat/client/ui/MainWindow.java Tue May 29 11:47:38 2012 -0400 +++ b/client/core/src/main/java/com/redhat/thermostat/client/ui/MainWindow.java Wed May 30 12:29:39 2012 -0400 @@ -74,6 +74,7 @@ import javax.swing.JTextField; import javax.swing.JTree; import javax.swing.KeyStroke; +import javax.swing.MenuElement; import javax.swing.SwingUtilities; import javax.swing.SwingWorker; import javax.swing.ToolTipManager; @@ -95,6 +96,7 @@ import com.redhat.thermostat.client.HostsVMsLoader; import com.redhat.thermostat.client.MainView; import com.redhat.thermostat.client.locale.LocaleResources; +import com.redhat.thermostat.client.osgi.service.MenuAction; import com.redhat.thermostat.client.osgi.service.VMContextAction; import com.redhat.thermostat.common.ActionListener; import com.redhat.thermostat.common.ActionNotifier; @@ -221,6 +223,7 @@ private static final long serialVersionUID = 5608972421496808177L; + private final JMenuBar mainMenuBar; private JPanel contentArea = null; private JTextField searchField = null; @@ -251,6 +254,8 @@ ToolTipManager.sharedInstance().registerComponent(agentVmTree); contentArea = new JPanel(new BorderLayout()); + mainMenuBar = new JMenuBar(); + setupMenus(); setupPanels(); @@ -280,7 +285,6 @@ } private void setupMenus() { - JMenuBar mainMenuBar = new JMenuBar(); JMenu fileMenu = new JMenu(localize(LocaleResources.MENU_FILE)); fileMenu.getPopupMenu().setBorder(BorderFactory.createLineBorder(Color.LIGHT_GRAY, 1)); @@ -623,6 +627,92 @@ }); } + @Override + public void addMenu(final String parentMenuName, final MenuAction action) { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + JMenu parent = null; + int mainMenuCount = mainMenuBar.getMenuCount(); + for (int i = 0; i < mainMenuCount; i++) { + if (mainMenuBar.getMenu(i).getText().equals(parentMenuName)) { + parent = mainMenuBar.getMenu(i); + break; + } + } + if (parent == null) { + parent = new JMenu(parentMenuName); + mainMenuBar.add(parent); + } + + JMenuItem menu = new JMenuItem(action.getName()); + menu.addActionListener(new java.awt.event.ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + action.execute(); + } + }); + parent.add(menu); + + mainMenuBar.revalidate(); + } + }); + } + + @Override + public void removeMenu(final String parentMenuName, final MenuAction action) { + final String actionName = action.getName(); + try { + new EdtHelper().callAndWait(new Runnable() { + @Override + public void run() { + MenuElement parent = null; + int mainMenuCount = mainMenuBar.getMenuCount(); + for (int i = 0; i < mainMenuCount; i++) { + if (mainMenuBar.getMenu(i).getText().equals(parentMenuName)) { + parent = mainMenuBar.getMenu(i); + break; + } + } + if (parent == null) { + throw new IllegalArgumentException("parent menu not found"); + } + + boolean removed = false; + MenuElement[] menus = parent.getSubElements(); + if (menus.length == 1 && (menus[0] instanceof JPopupMenu)) { + parent = menus[0]; + menus = parent.getSubElements(); + } + + for (MenuElement menu: menus) { + if (menu instanceof JMenuItem && ((JMenuItem)menu).getText().equals(actionName)) { + if (parent instanceof JPopupMenu) { + ((JPopupMenu)parent).remove((JMenuItem)menu); + removed = true; + } + } + } + + if (!removed) { + throw new IllegalArgumentException("child menu not found"); + } + + mainMenuBar.revalidate(); + } + }); + } catch (InterruptedException ie) { + Thread.currentThread().interrupt(); + throw new RuntimeException(ie); + } catch (InvocationTargetException roe) { + Throwable cause = roe.getCause(); + if (cause instanceof IllegalArgumentException) { + throw (IllegalArgumentException) cause; + } + throw new RuntimeException(cause); + } + } + /** * Returns null to indicate no Ref is selected */
--- a/client/core/src/test/java/com/redhat/thermostat/client/MainWindowControllerImplTest.java Tue May 29 11:47:38 2012 -0400 +++ b/client/core/src/test/java/com/redhat/thermostat/client/MainWindowControllerImplTest.java Wed May 30 12:29:39 2012 -0400 @@ -37,6 +37,7 @@ package com.redhat.thermostat.client; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyString; @@ -63,6 +64,8 @@ import org.osgi.framework.BundleContext; import org.osgi.framework.BundleException; +import com.redhat.thermostat.client.MenuRegistry.MenuListener; +import com.redhat.thermostat.client.osgi.service.MenuAction; import com.redhat.thermostat.client.osgi.service.VMContextAction; import com.redhat.thermostat.client.ui.SummaryController; import com.redhat.thermostat.client.ui.SummaryView; @@ -99,7 +102,9 @@ private VMContextAction action1; private VMContextAction action2; - + + private MenuListener menuListener; + @BeforeClass public static void setUpOnce() { // TODO remove when controller uses mocked objects rather than real swing objects @@ -129,6 +134,10 @@ ArgumentCaptor<ActionListener> grabListener = ArgumentCaptor.forClass(ActionListener.class); doNothing().when(view).addActionListener(grabListener.capture()); + MenuRegistry registry = mock(MenuRegistry.class); + ArgumentCaptor<MenuListener> menuListenerCaptor = ArgumentCaptor.forClass(MenuListener.class); + doNothing().when(registry).addMenuListener(menuListenerCaptor.capture()); + // TODO remove this asap. the main window has a hard dependency on summary controller/view ViewFactory viewFactory = mock(ViewFactory.class); SummaryView summaryView = mock(SummaryView.class); @@ -136,9 +145,10 @@ ApplicationContext.getInstance().setViewFactory(viewFactory); setUpVMContextActions(); - - controller = new MainWindowControllerImpl(uiFacadeFactory, view); + + controller = new MainWindowControllerImpl(uiFacadeFactory, view, registry); l = grabListener.getValue(); + menuListener = menuListenerCaptor.getValue(); } @@ -347,6 +357,19 @@ verify(action2, times(0)).execute(any(VmRef.class)); } + @Test + public void verifyMenuItems() { + assertNotNull(menuListener); + MenuAction action = mock(MenuAction.class); + when(action.getName()).thenReturn("Test1"); + + menuListener.added("File", action); + verify(view).addMenu("File", action); + + menuListener.removed("File", action); + verify(view).removeMenu("File", action); + } + @Test public void testOSGiFrameworkShutdown() throws BundleException {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/client/core/src/test/java/com/redhat/thermostat/client/MenuRegistryTest.java Wed May 30 12:29:39 2012 -0400 @@ -0,0 +1,90 @@ +/* + * 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.client; + +import static org.mockito.Matchers.eq; +import static org.mockito.Matchers.isA; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.osgi.framework.BundleContext; +import org.osgi.framework.InvalidSyntaxException; +import org.osgi.framework.ServiceEvent; +import org.osgi.framework.ServiceListener; +import org.osgi.framework.ServiceReference; + +import com.redhat.thermostat.client.MenuRegistry.MenuListener; +import com.redhat.thermostat.client.osgi.service.MenuAction; + +public class MenuRegistryTest { + + @Test + public void verifyMenuRegistryReactsToMenuActions() throws InvalidSyntaxException { + ArgumentCaptor<ServiceListener> serviceListenerCaptor = ArgumentCaptor.forClass(ServiceListener.class); + ArgumentCaptor<String> filterCaptor = ArgumentCaptor.forClass(String.class); + + MenuListener menuListener = mock(MenuListener.class); + MenuAction menuAction = mock(MenuAction.class); + + BundleContext context = mock(BundleContext.class); + doNothing().when(context).addServiceListener(serviceListenerCaptor.capture(), filterCaptor.capture()); + + ServiceReference ref = mock(ServiceReference.class); + when(ref.getProperty("objectClass")).thenReturn(MenuAction.class.getName()); + when(ref.getProperty(MenuRegistry.PARENT_MENU)).thenReturn("Test"); + + when(context.getService(ref)).thenReturn(menuAction); + + MenuRegistry registry = new MenuRegistry(context); + registry.addMenuListener(menuListener); + registry.start(); + + ServiceListener serviceListener = serviceListenerCaptor.getValue(); + serviceListener.serviceChanged(new ServiceEvent(ServiceEvent.REGISTERED, ref)); + + verify(menuListener).added(eq("Test"), isA(MenuAction.class)); + + serviceListener.serviceChanged(new ServiceEvent(ServiceEvent.UNREGISTERING, ref)); + + verify(menuListener).removed(eq("Test"), isA(MenuAction.class)); + + } +}
--- a/client/core/src/test/java/com/redhat/thermostat/client/ui/MainWindowTest.java Tue May 29 11:47:38 2012 -0400 +++ b/client/core/src/test/java/com/redhat/thermostat/client/ui/MainWindowTest.java Wed May 30 12:29:39 2012 -0400 @@ -37,15 +37,23 @@ package com.redhat.thermostat.client.ui; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.isA; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import javax.swing.AbstractAction; + import net.java.openjdk.cacio.ctc.junit.CacioFESTRunner; import org.fest.swing.annotation.GUITest; import org.fest.swing.edt.FailOnThreadViolationRepaintManager; import org.fest.swing.edt.GuiActionRunner; import org.fest.swing.edt.GuiTask; +import org.fest.swing.exception.ComponentLookupException; import org.fest.swing.fixture.FrameFixture; import org.fest.swing.fixture.JMenuItemFixture; import org.fest.swing.fixture.JTextComponentFixture; @@ -58,6 +66,7 @@ import org.junit.runner.RunWith; import com.redhat.thermostat.client.MainView; +import com.redhat.thermostat.client.osgi.service.MenuAction; import com.redhat.thermostat.common.ActionEvent; import com.redhat.thermostat.common.ActionListener; @@ -191,7 +200,38 @@ verify(l).actionPerformed(new ActionEvent<MainView.Action>(window, MainView.Action.SWITCH_HISTORY_MODE)); } - + + @Category(GUITest.class) + @Test + public void addRemoveMenu() { + final String MENU_NAME = "Test"; + MenuAction action = mock(MenuAction.class); + when(action.getName()).thenReturn(MENU_NAME); + + JMenuItemFixture menuItem; + + frameFixture.show(); + + window.addMenu("File", action); + + menuItem = frameFixture.menuItemWithPath("File", MENU_NAME); + assertNotNull(menuItem); + menuItem.click(); + + verify(action).execute(); + + window.removeMenu("File", action); + + try { + menuItem = frameFixture.menuItemWithPath("File", MENU_NAME); + // should not reach here + assertTrue(false); + } catch (ComponentLookupException cle) { + // expected + } + + } + @Category(GUITest.class) @Test public void testGetHostVMTreeFilter() {