/******************************************************************************* * Copyright (c) 2015, 2017 Pivotal, Inc. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Pivotal, Inc. - initial API and implementation *******************************************************************************/ package org.springframework.ide.eclipse.boot.dash.test; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import java.util.EnumSet; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.debug.core.DebugPlugin; import org.eclipse.debug.core.ILaunchConfiguration; import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jface.action.IAction; import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.springframework.ide.eclipse.boot.dash.model.AbstractLaunchConfigurationsDashElement; import org.springframework.ide.eclipse.boot.dash.model.BootDashElement; import org.springframework.ide.eclipse.boot.dash.model.RunState; import org.springframework.ide.eclipse.boot.dash.model.RunTarget; import org.springframework.ide.eclipse.boot.dash.model.RunTargets; import org.springframework.ide.eclipse.boot.dash.model.UserInteractions; import org.springframework.ide.eclipse.boot.dash.model.runtargettypes.RunTargetTypes; import org.springframework.ide.eclipse.boot.dash.test.mocks.MockMultiSelection; import org.springframework.ide.eclipse.boot.dash.views.BootDashActions; import org.springframework.ide.eclipse.boot.dash.views.BootDashActions.RunOrDebugStateAction; import org.springframework.ide.eclipse.boot.dash.views.DuplicateConfigAction; import org.springframework.ide.eclipse.boot.dash.views.OpenLaunchConfigAction; import org.springframework.ide.eclipse.boot.dash.views.RunStateAction; import org.springframework.ide.eclipse.boot.launch.BootLaunchConfigurationDelegate; import org.springframework.ide.eclipse.boot.launch.livebean.JmxBeanSupport; import org.springframework.ide.eclipse.boot.test.AutobuildingEnablement; import org.springframework.ide.eclipse.boot.test.BootProjectTestHarness; import org.springframework.ide.eclipse.boot.test.BootProjectTestHarness.WizardConfigurer; import org.springframework.ide.eclipse.boot.test.util.TestBracketter; import org.springsource.ide.eclipse.commons.frameworks.test.util.ACondition; import org.springsource.ide.eclipse.commons.tests.util.StsTestUtil; import com.google.common.collect.ImmutableSet; /** * @author Kris De Volder */ public class BootDashActionTests { @Test public void deleteConfigActionEnablementForProject() throws Exception { //At the moment, this action does not enable for projects at all String projectName = "hohoho"; IProject project = createBootProject(projectName); IJavaProject javaProject = JavaCore.create(project); final AbstractLaunchConfigurationsDashElement<?> element = (AbstractLaunchConfigurationsDashElement<?>) harness.getElementWithName(projectName); MockMultiSelection<BootDashElement> selection = harness.selection; BootLaunchConfigurationDelegate.createConf(javaProject); final IAction action = actions.getDeleteConfigsAction(); action.setEnabled(true); //force it to true so we can tell that it actually changes. selection.setElements(element); new ACondition("Wait for disablement", 3000) { public boolean test() throws Exception { assertFalse(action.isEnabled()); return true; } }; } @Test public void deleteConfigActionEnablementForConf() throws Exception { //At the moment, this action always enables for one or more launch confs String projectName = "hohoho"; IProject project = createBootProject(projectName); IJavaProject javaProject = JavaCore.create(project); final AbstractLaunchConfigurationsDashElement<?> element = (AbstractLaunchConfigurationsDashElement<?>) harness.getElementWithName(projectName); MockMultiSelection<BootDashElement> selection = harness.selection; final ILaunchConfiguration conf1 = BootLaunchConfigurationDelegate.createConf(javaProject); BootDashElement el1 = harness.getElementFor(conf1); final ILaunchConfiguration conf2 = BootLaunchConfigurationDelegate.createConf(javaProject); BootDashElement el2 = harness.getElementFor(conf2); new ACondition("Wait for children", 3000) { public boolean test() throws Exception { assertEquals(2, element.getCurrentChildren().size()); return true; } }; final IAction action = actions.getDeleteConfigsAction(); assertFalse(action.isEnabled()); selection.setElements(el1); new ACondition("Wait for enablement", 3000) { public boolean test() throws Exception { assertTrue(action.isEnabled()); return true; } }; action.setEnabled(false); selection.setElements(el1, el2); new ACondition("Wait for enablement", 3000) { public boolean test() throws Exception { assertTrue(action.isEnabled()); return true; } }; } @Test public void deleteConfigAction() throws Exception { String projectName = "hohoho"; IProject project = createBootProject(projectName); IJavaProject javaProject = JavaCore.create(project); final AbstractLaunchConfigurationsDashElement<?> element = (AbstractLaunchConfigurationsDashElement<?>) harness.getElementWithName(projectName); MockMultiSelection<BootDashElement> selection = harness.selection; final ILaunchConfiguration conf1 = BootLaunchConfigurationDelegate.createConf(javaProject); final BootDashElement el1 = harness.getElementFor(conf1); final ILaunchConfiguration conf2 = BootLaunchConfigurationDelegate.createConf(javaProject); final BootDashElement el2 = harness.getElementFor(conf2); new ACondition("Wait for children", 3000) { public boolean test() throws Exception { assertEquals(2, element.getCurrentChildren().size()); assertEquals(ImmutableSet.of(el1, el2), element.getCurrentChildren()); return true; } }; final IAction action = actions.getDeleteConfigsAction(); assertFalse(action.isEnabled()); selection.setElements(el1); new ACondition("Wait for enablement", 3000) { public boolean test() throws Exception { assertTrue(action.isEnabled()); return true; } }; when(ui.confirmOperation(eq("Deleting Elements"), anyString())).thenReturn(true); action.run(); new ACondition("Wait for config deletion", 3000) { public boolean test() throws Exception { assertEquals(ImmutableSet.of(conf2), element.getLaunchConfigs()); assertFalse(conf1.exists()); assertTrue(element.getCurrentChildren().size()==1); return true; } }; } @Test public void duplicateConfigAction() throws Exception { String projectName = "hohoho"; IProject project = createBootProject(projectName); IJavaProject javaProject = JavaCore.create(project); final AbstractLaunchConfigurationsDashElement<?> element = (AbstractLaunchConfigurationsDashElement<?>) harness.getElementWithName(projectName); MockMultiSelection<BootDashElement> selection = harness.selection; final DuplicateConfigAction action = actions.getDuplicateConfigAction(); final ILaunchConfiguration conf1 = BootLaunchConfigurationDelegate.createConf(javaProject); assertEquals(0, getJMXPortAsInt(conf1)); selection.setElements(element); new ACondition("Wait for enablement", 3000) { public boolean test() throws Exception { assertTrue(action.isEnabled()); return true; } }; action.run(); new ACondition("Wait for action post conditions", 3000) { public boolean test() throws Exception { ImmutableSet<ILaunchConfiguration> confs = element.getLaunchConfigs(); assertEquals(2, confs.size()); assertEquals(2, element.getCurrentChildren().size()); assertTrue(confs.contains(conf1)); for (ILaunchConfiguration other : confs) { assertEquals(0, getJMXPortAsInt(other)); } return true; } }; } @Test public void duplicateConfigActionWithJmxPortSet() throws Exception { String projectName = "hohoho"; IProject project = createBootProject(projectName); IJavaProject javaProject = JavaCore.create(project); final AbstractLaunchConfigurationsDashElement<?> element = (AbstractLaunchConfigurationsDashElement<?>) harness.getElementWithName(projectName); MockMultiSelection<BootDashElement> selection = harness.selection; final DuplicateConfigAction action = actions.getDuplicateConfigAction(); final ILaunchConfiguration conf1 = BootLaunchConfigurationDelegate.createConf(javaProject); assertEquals(0, getJMXPortAsInt(conf1)); String randomPort = ""+JmxBeanSupport.randomPort(); setJMXPort(conf1, randomPort); assertEquals(randomPort, BootLaunchConfigurationDelegate.getJMXPort(conf1)); selection.setElements(element); new ACondition("Wait for enablement", 3000) { public boolean test() throws Exception { assertTrue(action.isEnabled()); return true; } }; action.run(); new ACondition("Wait for action post conditions", 3000) { public boolean test() throws Exception { ImmutableSet<ILaunchConfiguration> confs = element.getLaunchConfigs(); assertEquals(2, confs.size()); assertEquals(2, element.getCurrentChildren().size()); assertTrue(confs.contains(conf1)); for (ILaunchConfiguration other : confs) { if (!other.equals(conf1)) { assertFalse(getJMXPortAsInt(conf1)==getJMXPortAsInt(other)); } } return true; } }; } private static void setJMXPort(ILaunchConfiguration conf, String port) throws CoreException { ILaunchConfigurationWorkingCopy wc = conf.getWorkingCopy(); BootLaunchConfigurationDelegate.setJMXPort(wc, port); wc.doSave(); } private static int getJMXPortAsInt(ILaunchConfiguration conf) { try { String str = BootLaunchConfigurationDelegate.getJMXPort(conf); if (str!=null) { return Integer.parseInt(str); } } catch (NumberFormatException e) { //couldn't parse } return -1; } @Test public void duplicateConfigActionEnablementForLaunchConf() throws Exception { String projectName = "hohoho"; IProject project = createBootProject(projectName); IJavaProject javaProject = JavaCore.create(project); final AbstractLaunchConfigurationsDashElement<?> element = (AbstractLaunchConfigurationsDashElement<?>) harness.getElementWithName(projectName); MockMultiSelection<BootDashElement> selection = harness.selection; final DuplicateConfigAction action = actions.getDuplicateConfigAction(); ILaunchConfiguration conf1 = BootLaunchConfigurationDelegate.createConf(javaProject); ILaunchConfiguration conf2 = BootLaunchConfigurationDelegate.createConf(javaProject); new ACondition("Wait for elements", 3000) { public boolean test() throws Exception { assertEquals(2, element.getLaunchConfigs().size()); assertEquals(2, element.getCurrentChildren().size()); return true; } }; BootDashElement el1 = harness.getElementFor(conf1); BootDashElement el2 = harness.getElementFor(conf2); assertFalse(action.isEnabled()); // or test may pass vacuously without an actual update selection.setElements(el1); new ACondition("Wait for enablement", 3000) { public boolean test() throws Exception { assertTrue(action.isEnabled()); return true; } }; selection.setElements(el1, el2); new ACondition("Wait for disablement", 3000) { public boolean test() throws Exception { assertFalse(action.isEnabled()); return true; } }; } @Test public void duplicateConfigActionEnablementForProject() throws Exception { String projectName = "hohoho"; IProject project = createBootProject(projectName); IJavaProject javaProject = JavaCore.create(project); final AbstractLaunchConfigurationsDashElement<?> element = (AbstractLaunchConfigurationsDashElement<?>) harness.getElementWithName(projectName); MockMultiSelection<BootDashElement> selection = harness.selection; final DuplicateConfigAction action = actions.getDuplicateConfigAction(); //If selection is empty the action must not be enabled assertTrue(selection.isEmpty()); assertFalse(action.isEnabled()); //If project is selected then... selection.setElements(element); // a) if project has no launch configs ... assertTrue(element.getLaunchConfigs().isEmpty()); // then there's nothing to duplicate... so disabled assertFalse(action.isEnabled()); // b) if project has exactly one launch config ... BootLaunchConfigurationDelegate.createConf(javaProject); // action enablement is updated as response to some asynchronous state changes // so may not happen immediately new ACondition("Wait for enablement", 3000) { public boolean test() throws Exception { assertEquals(1, element.getLaunchConfigs().size()); assertTrue(action.isEnabled()); return true; } }; // c) if project has more than one launch config... BootLaunchConfigurationDelegate.createConf(javaProject); ACondition.waitFor("Launch conf elements", 3000, () -> { assertEquals(2, element.getLaunchConfigs().size()); }); // ... async update may not happen right away... new ACondition("Wait for disablement", 3000) { public boolean test() throws Exception { assertEquals(2, element.getCurrentChildren().size()); assertFalse(action.isEnabled()); return true; } }; } @Test public void openConfigActionEnablementForProject() throws Exception { String projectName = "hohoho"; IProject project = createBootProject(projectName); IJavaProject javaProject = JavaCore.create(project); final BootDashElement element = harness.getElementWithName(projectName); MockMultiSelection<BootDashElement> selection = harness.selection; final OpenLaunchConfigAction action = actions.getOpenConfigAction(); //If selection is empty the action must not be enabled assertTrue(selection.isEmpty()); assertFalse(action.isEnabled()); //If selection has more than one element... the action must not be enabled selection.setElements(element, mockLocalElement()); assertFalse(action.isEnabled()); //If selection has one element... selection.setElements(element); //a) and element has no launch configs... assertTrue(element.getLaunchConfigs().isEmpty()); assertTrue(action.isEnabled()); // Careful... when changing the launch configs of a element, the enablement state of // action should auto-refresh, but this happens asyncly so the tests sequences are put in such // a order that the enablement state changes on each (otherwise the ACondition may vacuously // pass immediately even if the enablement didn't get updated, as it was correct from // the start) //b) and element has multiple launch config assertTrue(action.isEnabled()); // make sure the test won't pass 'by accident'. final ILaunchConfiguration c1 = BootLaunchConfigurationDelegate.createConf(javaProject); final ILaunchConfiguration c2 = BootLaunchConfigurationDelegate.createConf(javaProject); new ACondition(2000) { public boolean test() throws Exception { assertEquals(ImmutableSet.of(c1,c2), element.getLaunchConfigs()); assertFalse(action.isEnabled()); return true; } }; //b) and element has a single launch config assertFalse(action.isEnabled()); // make sure the test won't pass 'by accident'. c2.delete(); new ACondition(2000) { public boolean test() throws Exception { assertEquals(ImmutableSet.of(c1), element.getLaunchConfigs()); assertTrue(action.isEnabled()); return true; } }; } @Test public void openConfigActionEnablementForLaunchConfig() throws Exception { String projectName = "hohoho"; IProject project = createBootProject(projectName); IJavaProject javaProject = JavaCore.create(project); final BootDashElement element = harness.getElementWithName(projectName); MockMultiSelection<BootDashElement> selection = harness.selection; final OpenLaunchConfigAction action = actions.getOpenConfigAction(); BootLaunchConfigurationDelegate.createConf(javaProject); BootLaunchConfigurationDelegate.createConf(javaProject); //Check initial conditions are as expected: assertTrue(selection.isEmpty()); assertFalse(action.isEnabled()); ACondition.waitFor("children to appear", 3000, () -> { assertEquals(2, element.getChildren().getValues().size()); }); //Check action enablement for the children for (BootDashElement child : element.getChildren().getValues()) { selection.setElements(child); assertTrue(action.isEnabled()); } } @Test public void openRedebugActionEnablementForLaunchConfig() throws Exception { String projectName = "hohoho"; IProject project = createBootProject(projectName); IJavaProject javaProject = JavaCore.create(project); final BootDashElement element = harness.getElementWithName(projectName); MockMultiSelection<BootDashElement> selection = harness.selection; final RunStateAction action = getRunStateAction(RunState.DEBUGGING); BootLaunchConfigurationDelegate.createConf(javaProject); BootLaunchConfigurationDelegate.createConf(javaProject); //Check initial conditions are as expected: assertTrue(selection.isEmpty()); assertFalse(action.isEnabled()); ACondition.waitFor("children", 3000, () -> { assertEquals(2, element.getChildren().getValues().size()); }); //Check action enablement for the children for (BootDashElement child : element.getChildren().getValues()) { selection.setElements(child); assertTrue(action.isEnabled()); } } @Test public void redebugActionEnablementForMultipleProjects() throws Exception { IProject p1 = createBootProject("project1"); IProject p2 = createBootProject("project2"); BootDashElement e1 = harness.getElementWithName(p1.getName()); BootDashElement e2 = harness.getElementWithName(p2.getName()); MockMultiSelection<BootDashElement> selection = harness.selection; final RunStateAction action = getRunStateAction(RunState.DEBUGGING); assertTrue(selection.isEmpty()); assertFalse(action.isEnabled()); selection.setElements(e1, e2); assertTrue(action.isEnabled()); } @Test public void restartActionEnablementForProject() throws Exception { doRestartActionEnablementForProjectTest(RunState.RUNNING); } @Test public void redebugActionEnablementForProject() throws Exception { doRestartActionEnablementForProjectTest(RunState.DEBUGGING); } private void doRestartActionEnablementForProjectTest(RunState runOrDebug) throws Exception, CoreException { String projectName = "hohoho"; IProject project = createBootProject(projectName); IJavaProject javaProject = JavaCore.create(project); final BootDashElement element = harness.getElementWithName(projectName); MockMultiSelection<BootDashElement> selection = harness.selection; final RunStateAction action = getRunStateAction(runOrDebug); //If selection is empty the action must not be enabled assertTrue(selection.isEmpty()); assertFalse(action.isEnabled()); //If selection has one element... selection.setElements(element); //a) and element has no launch configs... assertTrue(element.getLaunchConfigs().isEmpty()); assertTrue(action.isEnabled()); //b) and element has multiple launch config action.setEnabled(false); // make sure the test won't pass 'by accident'. final ILaunchConfiguration c1 = BootLaunchConfigurationDelegate.createConf(javaProject); final ILaunchConfiguration c2 = BootLaunchConfigurationDelegate.createConf(javaProject); new ACondition(2000) { public boolean test() throws Exception { assertEquals(ImmutableSet.of(c1,c2), element.getLaunchConfigs()); assertTrue(action.isEnabled()); return true; } }; //b) and element has a single launch config action.setEnabled(false); // make sure the test won't pass 'by accident'. c2.delete(); new ACondition(2000) { public boolean test() throws Exception { assertEquals(ImmutableSet.of(c1), element.getLaunchConfigs()); assertTrue(action.isEnabled()); return true; } }; } @Test public void restartActionEnablementForMultipleProjects() throws Exception { IProject p1 = createBootProject("project1"); IProject p2 = createBootProject("project2"); BootDashElement e1 = harness.getElementWithName(p1.getName()); BootDashElement e2 = harness.getElementWithName(p2.getName()); MockMultiSelection<BootDashElement> selection = harness.selection; final RunStateAction action = getRunStateAction(RunState.RUNNING); assertTrue(selection.isEmpty()); assertFalse(action.isEnabled()); selection.setElements(e1, e2); assertTrue(action.isEnabled()); } @Test public void restartActionTargetsChildrenDirectly() throws Exception { String projectName = "hohoho"; IProject project = createBootProject(projectName); IJavaProject javaProject = JavaCore.create(project); MockMultiSelection<BootDashElement> selection = harness.selection; final BootDashElement element = harness.getElementWithName(projectName); final ILaunchConfiguration c1 = BootLaunchConfigurationDelegate.createConf(javaProject); final ILaunchConfiguration c2 = BootLaunchConfigurationDelegate.createConf(javaProject); BootDashElement child1 = harness.getElementFor(c1); BootDashElement child2 = harness.getElementFor(c2); ImmutableSet<BootDashElement> theChildren = ImmutableSet.of( child1, child2 ); ACondition.waitFor("children to appear", 3000, () -> { assertEquals(theChildren, element.getChildren().getValues()); }); for (RunState runOrDebug: EnumSet.of(RunState.RUNNING, RunState.DEBUGGING)) { final RunOrDebugStateAction action = (RunOrDebugStateAction) getRunStateAction(runOrDebug); selection.setElements(/*none*/); assertEquals(ImmutableSet.of(), action.getSelectedElements()); assertEquals(ImmutableSet.of(), action.getTargetElements()); selection.setElements(element); assertEquals(ImmutableSet.of(element), action.getSelectedElements()); assertEquals(""+runOrDebug, theChildren, action.getTargetElements()); selection.setElements(element, child1); assertEquals(ImmutableSet.of(element, child1), action.getSelectedElements()); assertEquals(theChildren, action.getTargetElements()); } } //////////////////////////////////////////////////////////////////////// private TestBootDashModelContext context; private BootProjectTestHarness projects; private BootDashViewModelHarness harness; private BootDashActions actions; private UserInteractions ui; @Rule public AutobuildingEnablement autobuild = new AutobuildingEnablement(false); @Rule public TestBracketter bracketter = new TestBracketter(); @Rule public LaunchCleanups launchCleanups = new LaunchCleanups(); @Before public void setup() throws Exception { StsTestUtil.deleteAllProjects(); this.context = new TestBootDashModelContext( ResourcesPlugin.getWorkspace(), DebugPlugin.getDefault().getLaunchManager() ); this.harness = new BootDashViewModelHarness(context, RunTargetTypes.LOCAL); this.projects = new BootProjectTestHarness(context.getWorkspace()); this.ui = mock(UserInteractions.class); this.actions = new BootDashActions(harness.model, harness.selection.forReading(), ui); } @After public void tearDown() throws Exception { this.harness.dispose(); this.actions.dispose(); } private IProject createBootProject(String projectName, WizardConfigurer... extraConfs) throws Exception { return projects.createBootWebProject(projectName, extraConfs); } private RunStateAction getRunStateAction(RunState goalState) { for (RunStateAction s : actions.getRunStateActions()) { if (s.getGoalState()==goalState) { return s; } } return null; } private BootDashElement mockLocalElement() { BootDashElement element = mock(BootDashElement.class); RunTarget target = RunTargets.LOCAL; when(element.getBootDashModel()).thenReturn(harness.getRunTargetModel(RunTargetTypes.LOCAL)); when(element.getTarget()).thenReturn(target); when(element.supportedGoalStates()).thenReturn(RunTargets.LOCAL_RUN_GOAL_STATES); return element; } }