/*******************************************************************************
* Copyright (c) 2015, 2016 GoPivotal, 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:
* GoPivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.boot.dash.test;
import static org.hamcrest.Matchers.arrayContaining;
import static org.hamcrest.Matchers.hasItems;
import static org.hamcrest.Matchers.stringContainsInOrder;
import static org.junit.Assert.assertEquals;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.argThat;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import java.util.Arrays;
import java.util.List;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.debug.core.ILaunch;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
import org.eclipse.debug.core.ILaunchManager;
import org.eclipse.debug.ui.IDebugUIConstants;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IType;
import org.hamcrest.Matcher;
import org.junit.Test;
import org.springframework.ide.eclipse.boot.dash.metadata.IPropertyStore;
import org.springframework.ide.eclipse.boot.dash.metadata.InMemoryPropertyStore;
import org.springframework.ide.eclipse.boot.dash.model.AbstractLaunchConfigurationsDashElement;
import org.springframework.ide.eclipse.boot.dash.model.LocalBootDashModel;
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.UserInteractions;
import org.springframework.ide.eclipse.boot.dash.ngrok.NGROKClient;
import org.springframework.ide.eclipse.boot.dash.ngrok.NGROKLaunchTracker;
import org.springframework.ide.eclipse.boot.dash.ngrok.NGROKTunnel;
import org.springframework.ide.eclipse.boot.dash.test.mocks.Mocks;
import org.springframework.ide.eclipse.boot.dash.util.LaunchConfRunStateTracker;
import com.google.common.collect.ImmutableSet;
/**
* Unit tests some of the methods that don't have great coverage yet
* after running BootDashModelTests. These methods are harder to test
* from the model (which does only minimal mocking).
*
* We perform focussed tests here using Mockito to essentially just test
* those methods in isolation here using mocks for anything it uses.
*
* @author Kris De Volder
*/
public class AbstractLaunchConfigurationsDashElementTest extends Mocks {
public static class TestElement extends AbstractLaunchConfigurationsDashElement<String> {
private IProject project;
public TestElement(String delegate, IProject project, LocalBootDashModel context) {
super(context, delegate);
this.project = project;
}
@Override
public void launch(String runMode, ILaunchConfiguration conf) {
}
@Override
public IType[] guessMainTypes() throws CoreException {
return NO_TYPES;
}
@Override
protected IPropertyStore createPropertyStore() {
return new InMemoryPropertyStore();
}
@Override
public ImmutableSet<ILaunchConfiguration> getLaunchConfigs() {
return ImmutableSet.of();
}
@Override
public IProject getProject() {
return project;
}
@Override
public String getName() {
return delegate;
}
@Override
public ImmutableSet<ILaunch> getLaunches() {
return ImmutableSet.of();
}
@Override
public Object getParent() {
return getBootDashModel();
}
}
private static final IType[] NO_TYPES = {};
public static TestElement createElement(String testElementName, IProject project, IJavaProject javaProject, RunTarget runTarget) {
// BootProjectDashElementFactory factory = mock(BootProjectDashElementFactory.class);
// LaunchConfDashElementFactory childFactory = mock(LaunchConfDashElementFactory.class);
// LaunchConfRunStateTracker runStateTracker = mock(LaunchConfRunStateTracker.class);
// when(model.getLaunchConfRunStateTracker()).thenReturn(runStateTracker);
// IProject project = javaProject.getProject();
LocalBootDashModel model = mock(LocalBootDashModel.class);
LaunchConfRunStateTracker runStateTracker = mock(LaunchConfRunStateTracker.class);
when(model.getLaunchConfRunStateTracker()).thenReturn(runStateTracker);
TestElement element = spy(new TestElement(testElementName, project, model));
when(element.getTarget()).thenReturn(runTarget);
doReturn(javaProject).when(element).getJavaProject();
return element;
}
public static IType mockType(IJavaProject javaProject, String pkg, String name) {
IType type = mock(IType.class);
when(type.getElementName()).thenReturn(name);
when(type.getFullyQualifiedName()).thenReturn(pkg+"."+name);
when(type.getJavaProject()).thenReturn(javaProject);
return type;
}
@Test(expected=IllegalArgumentException.class)
public void restartWithBadArgument() throws Exception {
String projectName = "fooProject";
IProject project = mockProject(projectName, true);
IJavaProject javaProject = mockJavaProject(project);
RunTarget runTarget = mock(RunTarget.class);
TestElement element = createElement(projectName, project, javaProject, runTarget);
UserInteractions ui = mock(UserInteractions.class);
element.restart(RunState.INACTIVE, ui);
}
public static IJavaProject mockJavaProject(IProject project) {
String projectName = project.getName();
IJavaProject jp = mock(IJavaProject.class);
when(jp.getElementName()).thenReturn(projectName);
when(jp.getProject()).thenReturn(project);
return jp;
}
@Test
public void restartNoMainTypes() throws Exception {
String projectName = "fooProject";
IProject project = mockProject(projectName, true);
IJavaProject javaProject = mockJavaProject(project);
RunTarget runTarget = mock(RunTarget.class);
TestElement element = createElement(projectName, project, javaProject, runTarget);
UserInteractions ui = mock(UserInteractions.class);
when(element.guessMainTypes()).thenReturn(NO_TYPES);
element.restart(RunState.RUNNING, ui);
verify(element).stopSync();
verify(ui).errorPopup(
stringContains("Problem"),
stringContains("Couldn't find a main type")
);
verifyNoMoreInteractions(ui);
}
@Test
public void restartOneMainType() throws Exception {
String projectName = "fooProject";
IProject project = mockProject(projectName, true);
IJavaProject javaProject = mockJavaProject(project);
RunTarget runTarget = mock(RunTarget.class);
TestElement element = createElement(projectName, project, javaProject, runTarget);
UserInteractions ui = mock(UserInteractions.class);
ILaunchConfiguration conf = mock(ILaunchConfiguration.class);
IType type = mockType(javaProject, "demo", "FooApplication");
when(element.guessMainTypes()).thenReturn(new IType[] {type});
when(runTarget.createLaunchConfig(javaProject, type)).thenReturn(conf);
element.restart(RunState.RUNNING, ui);
verify(element).stopSync();;
verify(element).launch(ILaunchManager.RUN_MODE, conf);
verifyZeroInteractions(ui);
}
@Test
public void restartAndExpose() throws Exception {
String projectName = "fooProject";
IProject project = mockProject(projectName, true);
IJavaProject javaProject = mockJavaProject(project);
RunTarget runTarget = mock(RunTarget.class);
TestElement element = createElement(projectName, project, javaProject, runTarget);
UserInteractions ui = mock(UserInteractions.class);
ILaunchConfiguration conf = mock(ILaunchConfiguration.class);
ILaunchConfigurationWorkingCopy copyOfConf = mock(ILaunchConfigurationWorkingCopy.class);
IType type = mockType(javaProject, "demo", "FooApplication");
NGROKClient ngrokClient = mock(NGROKClient.class);
NGROKTunnel tunnel = new NGROKTunnel("foo-launch", "http", "publicURLTest", "8888");
when(element.guessMainTypes()).thenReturn(new IType[] {type});
when(runTarget.createLaunchConfig(javaProject, type)).thenReturn(conf);
when(conf.getWorkingCopy()).thenReturn(copyOfConf);
when(element.getLivePort()).thenReturn(8888);
when(ngrokClient.startTunnel("http", "8888")).thenReturn(tunnel);
when(conf.getName()).thenReturn("foo-launch");
when(copyOfConf.getName()).thenReturn("foo-launch");
String eurekaInstance = "eureka instance somewhere";
element.restartAndExpose(RunState.RUNNING, ngrokClient, eurekaInstance, ui);
verify(element).stopSync();;
verify(element).launch(ILaunchManager.RUN_MODE, copyOfConf);
verifyZeroInteractions(ui);
verify(copyOfConf).setAttribute("spring.boot.prop.server.port", "18888");
verify(copyOfConf).setAttribute("spring.boot.prop.eureka.instance.hostname", "1publicURLTest");
verify(copyOfConf).setAttribute("spring.boot.prop.eureka.instance.nonSecurePort", "180");
verify(copyOfConf).setAttribute("spring.boot.prop.eureka.client.service-url.defaultZone", "1" + eurekaInstance);
NGROKClient storedNgrokClient = NGROKLaunchTracker.get("foo-launch");
assertEquals(storedNgrokClient, ngrokClient);
NGROKLaunchTracker.remove("foo-launch");
}
@Test
public void restartTwoMainTypes() throws Exception {
String projectName = "fooProject";
IProject project = mockProject(projectName, true);
IJavaProject javaProject = mockJavaProject(project);
RunTarget runTarget = mock(RunTarget.class);
TestElement element = createElement(projectName, project, javaProject, runTarget);
UserInteractions ui = mock(UserInteractions.class);
ILaunchConfiguration conf = mock(ILaunchConfiguration.class);
IType fooType = mockType(javaProject, "demo", "FooApplication");
IType barType = mockType(javaProject, "demo", "BarApplication");
when(element.guessMainTypes()).thenReturn(new IType[] {fooType, barType});
when(ui.chooseMainType(
argThat(arrayContaining(fooType, barType)),
any(String.class),
any(String.class)
)).thenReturn(barType);
when(runTarget.createLaunchConfig(javaProject, barType)).thenReturn(conf);
element.restart(RunState.RUNNING, ui);
verify(element).stopSync();
verify(ui).chooseMainType(any(IType[].class),
stringContains("Choose"),
stringContains("Choose", projectName)
);
verify(element).launch(ILaunchManager.RUN_MODE, conf);
verifyNoMoreInteractions(ui);
}
@Test
public void openConfigWithNoExistingConfs() throws Exception {
String projectName = "fooProject";
IProject project = mockProject(projectName, true);
IJavaProject javaProject = mockJavaProject(project);
RunTarget runTarget = mock(RunTarget.class);
TestElement element = createElement(projectName, project, javaProject, runTarget);
UserInteractions ui = mock(UserInteractions.class);
ILaunchConfiguration conf = mock(ILaunchConfiguration.class);
when(runTarget.createLaunchConfig(javaProject, null)).thenReturn(conf);
doReturn(RunState.INACTIVE).when(element).getRunState();
element.openConfig(ui);
verify(ui).openLaunchConfigurationDialogOnGroup(conf, IDebugUIConstants.ID_DEBUG_LAUNCH_GROUP);
verifyNoMoreInteractions(ui);
}
@Test
public void openConfigWithOneExistingConfs() throws Exception {
String projectName = "fooProject";
IProject project = mockProject(projectName, true);
IJavaProject javaProject = mockJavaProject(project);
RunTarget runTarget = mock(RunTarget.class);
TestElement element = createElement(projectName, project, javaProject, runTarget);
UserInteractions ui = mock(UserInteractions.class);
ILaunchConfiguration conf = mock(ILaunchConfiguration.class);
when(element.getLaunchConfigs()).thenReturn(ImmutableSet.of(conf));
doReturn(RunState.INACTIVE).when(element).getRunState();
element.openConfig(ui);
verify(ui).openLaunchConfigurationDialogOnGroup(conf, IDebugUIConstants.ID_DEBUG_LAUNCH_GROUP);
verifyNoMoreInteractions(ui);
}
@SuppressWarnings("unchecked")
@Test
public void openConfigWithTwoExistingConfs() throws Exception {
String projectName = "fooProject";
IProject project = mockProject(projectName, true);
IJavaProject javaProject = mockJavaProject(project);
RunTarget runTarget = mock(RunTarget.class);
TestElement element = createElement(projectName, project, javaProject, runTarget);
UserInteractions ui = mock(UserInteractions.class);
ILaunchConfiguration conf1 = mock(ILaunchConfiguration.class);
ILaunchConfiguration conf2 = mock(ILaunchConfiguration.class);
when(element.getLaunchConfigs()).thenReturn(ImmutableSet.of(conf1, conf2));
doReturn(RunState.INACTIVE).when(element).getRunState();
when(ui.chooseConfigurationDialog(anyString(), anyString(), listThat(hasItems(conf1, conf2))))
.thenReturn(conf2);
element.openConfig(ui);
verify(ui).chooseConfigurationDialog(
stringContains("Choose", "Configuration"),
stringContains("Several"),
(List<ILaunchConfiguration>) any()
);
verify(ui).openLaunchConfigurationDialogOnGroup(conf2, IDebugUIConstants.ID_DEBUG_LAUNCH_GROUP);
verifyNoMoreInteractions(ui);
}
private <T> List<T> listThat(Matcher<Iterable<T>> iterMatcher) {
Object untyped = iterMatcher;
@SuppressWarnings("unchecked")
Matcher<List<T>> listMatcher = (Matcher<List<T>>) untyped;
return argThat(listMatcher);
}
public static String stringContains(String... strings) {
return argThat(stringContainsInOrder(Arrays.asList(strings)));
}
}