diff --git a/plugins/com.google.cloud.tools.eclipse.appengine.localserver/src/com/google/cloud/tools/eclipse/appengine/localserver/server/LocalAppEngineServerLaunchConfigurationDelegate.java b/plugins/com.google.cloud.tools.eclipse.appengine.localserver/src/com/google/cloud/tools/eclipse/appengine/localserver/server/LocalAppEngineServerLaunchConfigurationDelegate.java
index 89650b8fe0..d04d4b466f 100644
--- a/plugins/com.google.cloud.tools.eclipse.appengine.localserver/src/com/google/cloud/tools/eclipse/appengine/localserver/server/LocalAppEngineServerLaunchConfigurationDelegate.java
+++ b/plugins/com.google.cloud.tools.eclipse.appengine.localserver/src/com/google/cloud/tools/eclipse/appengine/localserver/server/LocalAppEngineServerLaunchConfigurationDelegate.java
@@ -448,7 +448,8 @@ public void launch(ILaunchConfiguration configuration, String mode, final ILaunc
generateServerRunConfiguration(configuration, server, mode, runnables);
if (ILaunchManager.DEBUG_MODE.equals(mode)) {
int debugPort = getDebugPort();
- setupDebugTarget(devServerRunConfiguration, launch, debugPort, monitor);
+ devServerRunConfiguration =
+ setupDebugTarget(devServerRunConfiguration, launch, debugPort, monitor);
}
IJavaProject javaProject = JavaCore.create(modules[0].getProject());
@@ -492,8 +493,13 @@ protected void openBrowserPage(final IServer server) {
server.getName());
}
- private void setupDebugTarget(RunConfiguration devServerRunConfiguration, ILaunch launch,
- int debugPort, IProgressMonitor monitor) throws CoreException {
+ /** Set up the debug target to connect to the remote JVM. Returns the updated RunConfiguration. */
+ private RunConfiguration setupDebugTarget(
+ RunConfiguration devServerRunConfiguration,
+ ILaunch launch,
+ int debugPort,
+ IProgressMonitor monitor)
+ throws CoreException {
if (debugPort <= 0 || debugPort > 65535) {
throw new IllegalArgumentException("Debug port is set to " + debugPort //$NON-NLS-1$
+ ", should be between 1-65535"); //$NON-NLS-1$
@@ -511,7 +517,8 @@ private void setupDebugTarget(RunConfiguration devServerRunConfiguration, ILaunc
JavaRuntime.getVMConnector(IJavaLaunchConfigurationConstants.ID_SOCKET_LISTEN_VM_CONNECTOR);
if (connector == null) {
abort("Cannot find Socket Listening connector", null, 0); //$NON-NLS-1$
- return; // keep JDT null analysis happy
+ // NOTREACHED
+ return null; // keep JDT null analysis happy
}
// Set JVM debugger connection parameters
@@ -523,6 +530,7 @@ private void setupDebugTarget(RunConfiguration devServerRunConfiguration, ILaunc
connectionParameters.put("timeout", Integer.toString(timeout)); //$NON-NLS-1$
connectionParameters.put("connectionLimit", "0"); //$NON-NLS-1$ //$NON-NLS-2$
connector.connect(connectionParameters, monitor, launch);
+ return devServerRunConfiguration;
}
private int getDebugPort() throws CoreException {
diff --git a/plugins/com.google.cloud.tools.eclipse.integration.appengine/src/com/google/cloud/tools/eclipse/integration/appengine/DebugNativeAppEngineStandardProjectTest.java b/plugins/com.google.cloud.tools.eclipse.integration.appengine/src/com/google/cloud/tools/eclipse/integration/appengine/DebugNativeAppEngineStandardProjectTest.java
index b2b0ee56b0..de302462f0 100644
--- a/plugins/com.google.cloud.tools.eclipse.integration.appengine/src/com/google/cloud/tools/eclipse/integration/appengine/DebugNativeAppEngineStandardProjectTest.java
+++ b/plugins/com.google.cloud.tools.eclipse.integration.appengine/src/com/google/cloud/tools/eclipse/integration/appengine/DebugNativeAppEngineStandardProjectTest.java
@@ -19,6 +19,7 @@
import static org.eclipse.swtbot.swt.finder.matchers.WidgetMatcherFactory.widgetOfType;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.endsWith;
+import static org.hamcrest.Matchers.stringContainsInOrder;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
@@ -35,6 +36,7 @@
import java.net.URL;
import java.net.URLConnection;
import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
import java.util.concurrent.TimeUnit;
import org.eclipse.core.runtime.preferences.InstanceScope;
import org.eclipse.swt.custom.StyledText;
@@ -59,15 +61,15 @@ public class DebugNativeAppEngineStandardProjectTest extends BaseProjectTest {
private static final long TERMINATE_SERVER_TIMEOUT = 10000L;
- @Rule
- public ThreadDumpingWatchdog timer = new ThreadDumpingWatchdog(2, TimeUnit.MINUTES);
+ @Rule public ThreadDumpingWatchdog timer = new ThreadDumpingWatchdog(2, TimeUnit.MINUTES);
/**
* Launch a native application in debug mode and verify that:
+ *
*
- * - it started,
- * - it can be terminated and removed from the launch list, and
- * - the process is actually terminated.
+ * - it started,
+ *
- it can be terminated and removed from the launch list, and
+ *
- the process is actually terminated.
*
*/
@Test
@@ -80,8 +82,9 @@ public void testDebugLaunch() throws Exception {
assertNoService(new URL("http://localhost:8080/hello"));
- project = SwtBotAppEngineActions.createNativeWebAppProject(bot, "testapp_java8", null,
- "app.engine.test", null /* runtime */);
+ project =
+ SwtBotAppEngineActions.createNativeWebAppProject(
+ bot, "testapp_java8", null, "app.engine.test", null /* runtime */);
assertTrue(project.exists());
SWTBotTreeItem testProject = SwtBotProjectActions.selectProject(bot, "testapp_java8");
@@ -96,8 +99,10 @@ public void testDebugLaunch() throws Exception {
SwtBotTestingUtilities.clickButtonAndWaitForWindowClose(bot, bot.button("Finish"));
- bot.perspectiveById("org.eclipse.debug.ui.DebugPerspective").activate(); // IDebugUIConstants.ID_DEBUG_PERSPECTIVE
- SWTBotView debugView = bot.viewById("org.eclipse.debug.ui.DebugView"); // IDebugUIConstants.ID_DEBUG_VIEW
+ bot.perspectiveById("org.eclipse.debug.ui.DebugPerspective")
+ .activate(); // IDebugUIConstants.ID_DEBUG_PERSPECTIVE
+ SWTBotView debugView =
+ bot.viewById("org.eclipse.debug.ui.DebugView"); // IDebugUIConstants.ID_DEBUG_VIEW
debugView.show();
SWTBotTree launchTree =
@@ -111,22 +116,30 @@ public void testDebugLaunch() throws Exception {
SwtBotTreeUtilities.waitUntilTreeHasItems(bot, launchTree);
SWTBotTreeItem[] allItems = launchTree.getAllItems();
- SwtBotTreeUtilities.waitUntilTreeContainsText(bot, allItems[0],
- "App Engine Standard at localhost");
+ SwtBotTreeUtilities.waitUntilTreeContainsText(
+ bot, allItems[0], "App Engine Standard at localhost");
- SWTBotView consoleView = bot.viewById("org.eclipse.ui.console.ConsoleView"); // IConsoleConstants.ID_CONSOLE_VIEW
+ SWTBotView consoleView =
+ bot.viewById("org.eclipse.ui.console.ConsoleView"); // IConsoleConstants.ID_CONSOLE_VIEW
consoleView.show();
SwtBotTestingUtilities.waitUntilViewContentDescription(
bot, consoleView, Matchers.containsString("App Engine Standard at localhost"));
SWTBotStyledText consoleContents =
new SWTBotStyledText(bot.widget(widgetOfType(StyledText.class), consoleView.getWidget()));
- SwtBotTestingUtilities.waitUntilStyledTextContains(bot,
- "Module instance default is running at http://localhost:8080", consoleContents);
+ SwtBotTestingUtilities.waitUntilStyledTextContains(
+ bot, "Module instance default is running at http://localhost:8080", consoleContents);
// Server is now running
- assertEquals("Hello App Engine!",
+ assertEquals(
+ "Hello App Engine!",
getUrlContents(new URL("http://localhost:8080/hello"), (int) SWTBotPreferences.TIMEOUT));
+ // Ensure debugger has connected by looking for well-known thread
+ debugView.show();
+ assertTrue(
+ SwtBotTreeUtilities.hasChild(
+ bot, launchTree, stringContainsInOrder(Arrays.asList("Thread", "(Running)"))));
+
{
SWTBotView serversView = bot.viewById("org.eclipse.wst.server.ui.ServersView");
serversView.show();
@@ -149,21 +162,21 @@ public void testDebugLaunch() throws Exception {
SwtBotTreeUtilities.waitUntilTreeTextMatches(
bot, allItems[0], containsString(""), TERMINATE_SERVER_TIMEOUT);
assertNoService(new URL("http://localhost:8080/hello"));
- assertTrue("App Engine console should mark as stopped",
+ assertTrue(
+ "App Engine console should mark as stopped",
consoleView.getViewReference().getContentDescription().startsWith(""));
assertFalse("Stop Server button should be disabled", stopServerButton.isEnabled());
// should also cause console to be discarded
launchTree.contextMenu("Remove All Terminated").click();
SwtBotTreeUtilities.waitUntilTreeHasNoItems(bot, launchTree);
- assertThat("App Engine console should be removed",
+ assertThat(
+ "App Engine console should be removed",
consoleView.getViewReference().getContentDescription(),
Matchers.is("No consoles to display at this time."));
}
- /**
- * Check that there is no remote service for the URL.
- */
+ /** Check that there is no remote service for the URL. */
private void assertNoService(URL url) {
try {
getUrlContents(url, 10);
@@ -193,5 +206,4 @@ private static String getUrlContents(URL url, int timeoutInMilliseconds) throws
}
return content.toString().trim();
}
-
}
diff --git a/plugins/com.google.cloud.tools.eclipse.swtbot/src/com/google/cloud/tools/eclipse/swtbot/SwtBotTreeUtilities.java b/plugins/com.google.cloud.tools.eclipse.swtbot/src/com/google/cloud/tools/eclipse/swtbot/SwtBotTreeUtilities.java
index cf102c65af..d8b1b3d2b6 100644
--- a/plugins/com.google.cloud.tools.eclipse.swtbot/src/com/google/cloud/tools/eclipse/swtbot/SwtBotTreeUtilities.java
+++ b/plugins/com.google.cloud.tools.eclipse.swtbot/src/com/google/cloud/tools/eclipse/swtbot/SwtBotTreeUtilities.java
@@ -16,11 +16,18 @@
package com.google.cloud.tools.eclipse.swtbot;
+import static org.junit.Assert.assertFalse;
+
import com.google.common.base.Preconditions;
import com.google.common.collect.Iterables;
import java.util.Arrays;
+import java.util.Collections;
+import java.util.LinkedList;
+import org.eclipse.swt.widgets.TreeItem;
import org.eclipse.swtbot.eclipse.finder.SWTWorkbenchBot;
import org.eclipse.swtbot.swt.finder.exceptions.WidgetNotFoundException;
+import org.eclipse.swtbot.swt.finder.finders.UIThreadRunnable;
+import org.eclipse.swtbot.swt.finder.results.Result;
import org.eclipse.swtbot.swt.finder.utils.SWTBotPreferences;
import org.eclipse.swtbot.swt.finder.waits.DefaultCondition;
import org.eclipse.swtbot.swt.finder.widgets.SWTBotTree;
@@ -31,9 +38,7 @@
import org.hamcrest.Matchers;
import org.hamcrest.StringDescription;
-/**
- * Utilities for manipulating trees.
- */
+/** Utilities for manipulating trees. */
public class SwtBotTreeUtilities {
/**
@@ -42,17 +47,18 @@ public class SwtBotTreeUtilities {
* @throws TimeoutException if no items appear within the default timeout
*/
public static SWTBotTreeItem waitUntilTreeHasItems(SWTWorkbenchBot bot, SWTBotTree tree) {
- bot.waitUntil(new DefaultCondition() {
- @Override
- public String getFailureMessage() {
- return "Tree items never appeared";
- }
+ bot.waitUntil(
+ new DefaultCondition() {
+ @Override
+ public String getFailureMessage() {
+ return "Tree items never appeared";
+ }
- @Override
- public boolean test() throws Exception {
- return tree.hasItems();
- }
- });
+ @Override
+ public boolean test() throws Exception {
+ return tree.hasItems();
+ }
+ });
return tree.getAllItems()[0];
}
@@ -122,21 +128,22 @@ public boolean test() throws Exception {
/**
* Wait until the given tree has not items.
- *
+ *
* @throws TimeoutException if no items appear within the default timeout
*/
public static void waitUntilTreeHasNoItems(SWTWorkbenchBot bot, final SWTBotTree tree) {
- bot.waitUntil(new DefaultCondition() {
- @Override
- public String getFailureMessage() {
- return "Tree items never disappeared";
- }
+ bot.waitUntil(
+ new DefaultCondition() {
+ @Override
+ public String getFailureMessage() {
+ return "Tree items never disappeared";
+ }
- @Override
- public boolean test() throws Exception {
- return !tree.hasItems();
- }
- });
+ @Override
+ public boolean test() throws Exception {
+ return !tree.hasItems();
+ }
+ });
}
/**
@@ -227,4 +234,39 @@ public static SWTBotTreeItem select(SWTWorkbenchBot bot, SWTBotTree tree, String
}
return item.getNode(nodeNames[leafIndex]).select(); // throws WNFE
}
+
+ /** Expand the tree as necessary to find a child matching the given condition. */
+ public static boolean hasChild(SWTWorkbenchBot bot, SWTBotTree tree, Matcher textMatcher) {
+ waitUntilTreeHasItems(bot, tree);
+ // perform breadth-first search; execute directly in SWT thread as the tree may otherwise
+ // be affected by thread changes
+ Result query =
+ () -> {
+ TreeItem[] items = tree.widget.getItems();
+ for (TreeItem item : items) {
+ if (textMatcher.matches(item.getText())) {
+ return true;
+ }
+ }
+ LinkedList stack = new LinkedList<>();
+ Collections.addAll(stack, items);
+ while (!stack.isEmpty()) {
+ TreeItem parent = stack.removeFirst();
+ items = parent.getItems();
+ // If this assertion fails, it may be due to
+ // https://github.com/GoogleCloudPlatform/google-cloud-eclipse/issues/2569
+ // and may require applying the workaround to collapse and re-expand the node
+ assertFalse(
+ "workaround may be required", items.length == 1 && "".equals(items[0].getText()));
+ for (TreeItem item : items) {
+ if (textMatcher.matches(item.getText())) {
+ return true;
+ }
+ }
+ Collections.addAll(stack, items);
+ }
+ return false;
+ };
+ return UIThreadRunnable.syncExec(query);
+ }
}
diff --git a/pom.xml b/pom.xml
index baf81fe9fa..2bd19cf664 100644
--- a/pom.xml
+++ b/pom.xml
@@ -329,6 +329,7 @@
hourly
600
+ false