/******************************************************************************* * Copyright (c) 2007-2017 Red Hat, Inc. * Distributed under license by Red Hat, Inc. All rights reserved. * This program is made available under the terms of the * Eclipse Public License v 1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/epl-v10.html * * Contributor: * Red Hat, Inc. - initial API and implementation ******************************************************************************/ package org.jboss.tools.openshift.ui.bot.test.application.v3.adapter; import static org.junit.Assert.assertTrue; import java.util.List; import java.util.function.Predicate; import org.jboss.reddeer.common.condition.AbstractWaitCondition; import org.jboss.reddeer.common.exception.WaitTimeoutExpiredException; import org.jboss.reddeer.common.logging.Logger; import org.jboss.reddeer.common.wait.TimePeriod; import org.jboss.reddeer.common.wait.WaitUntil; import org.jboss.reddeer.common.wait.WaitWhile; import org.jboss.reddeer.core.condition.JobIsRunning; import org.jboss.reddeer.core.exception.CoreLayerException; import org.jboss.reddeer.eclipse.condition.ServerHasPublishState; import org.jboss.reddeer.eclipse.condition.ServerHasState; import org.jboss.reddeer.eclipse.core.resources.Project; import org.jboss.reddeer.eclipse.core.resources.ProjectItem; import org.jboss.reddeer.eclipse.debug.core.BreakpointsView; import org.jboss.reddeer.eclipse.debug.core.DebugView; import org.jboss.reddeer.eclipse.debug.core.VariablesView; import org.jboss.reddeer.eclipse.jdt.ui.ProjectExplorer; import org.jboss.reddeer.eclipse.ui.browser.BrowserEditor; import org.jboss.reddeer.eclipse.ui.console.ConsoleView; import org.jboss.reddeer.eclipse.ui.perspectives.DebugPerspective; import org.jboss.reddeer.eclipse.wst.server.ui.view.ServersView; import org.jboss.reddeer.eclipse.wst.server.ui.view.ServersViewEnums.ServerPublishState; import org.jboss.reddeer.eclipse.wst.server.ui.view.ServersViewEnums.ServerState; import org.jboss.reddeer.junit.requirement.inject.InjectRequirement; import org.jboss.reddeer.junit.runner.RedDeerSuite; import org.jboss.reddeer.junit.screenshot.CaptureScreenshotException; import org.jboss.reddeer.requirements.openperspective.OpenPerspectiveRequirement.OpenPerspective; import org.jboss.reddeer.swt.api.TreeItem; import org.jboss.reddeer.swt.impl.button.OkButton; import org.jboss.reddeer.swt.impl.menu.ContextMenu; import org.jboss.reddeer.swt.impl.menu.ShellMenu; import org.jboss.reddeer.swt.impl.shell.DefaultShell; import org.jboss.reddeer.swt.impl.styledtext.DefaultStyledText; import org.jboss.reddeer.swt.impl.toolbar.DefaultToolItem; import org.jboss.reddeer.swt.impl.tree.DefaultTree; import org.jboss.reddeer.swt.impl.tree.DefaultTreeItem; import org.jboss.reddeer.workbench.exception.WorkbenchLayerException; import org.jboss.reddeer.workbench.impl.editor.TextEditor; import org.jboss.reddeer.workbench.impl.shell.WorkbenchShell; import org.jboss.reddeer.workbench.ui.dialogs.WorkbenchPreferenceDialog; import org.jboss.tools.openshift.reddeer.preference.page.JavaDebugPreferencePage; import org.jboss.tools.openshift.reddeer.requirement.OpenShiftCommandLineToolsRequirement.OCBinary; import org.jboss.tools.openshift.reddeer.requirement.OpenShiftConnectionRequirement; import org.jboss.tools.openshift.reddeer.requirement.OpenShiftConnectionRequirement.RequiredBasicConnection; import org.jboss.tools.openshift.reddeer.view.OpenShiftExplorerView; import org.jboss.tools.openshift.reddeer.view.resources.ServerAdapter; import org.jboss.tools.openshift.reddeer.view.resources.ServerAdapter.Version; import org.jboss.tools.openshift.ui.bot.test.application.v3.adapter.condition.BrowserIsReadyElseReloadCondition; import org.jboss.tools.openshift.ui.bot.test.application.v3.adapter.condition.SuspendedTreeItemIsReady; import org.jboss.tools.openshift.ui.bot.test.application.v3.create.AbstractCreateApplicationTest; import org.junit.After; import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; @OpenPerspective(DebugPerspective.class) @RunWith(RedDeerSuite.class) @OCBinary @RequiredBasicConnection public class DebuggingEAPAppTest extends AbstractCreateApplicationTest { private static Logger LOGGER = new Logger(DebuggingEAPAppTest.class); @InjectRequirement private OpenShiftConnectionRequirement requiredConnection; private static ServerAdapter serverAdapter; @BeforeClass public static void setupClass() { doNotSuspendOnUncaughtExceptions(); toggleAutoBuild(false); createServerAdapter(); disableShowConsoleWhenOutputChanges(); serverAdapter = new ServerAdapter(Version.OPENSHIFT3, "eap-app", "Service"); serverAdapter.select(); new ContextMenu("Restart in Debug").select(); new WaitWhile(new JobIsRunning(), TimePeriod.VERY_LONG); waitForserverAdapterToBeInRightState(); cleanAndBuildWorkspace(); } @AfterClass public static void tearDownClass() { toggleAutoBuild(true); } @Before public void setup() { setupBreakpoint(); cleanAndBuildWorkspace(); triggerDebugSession(); } @Test public void debuggerStopsAtBreakpointTest() { // now it shoud be stopped in debug mode. checkDebugView(); checkVariablesView(); } @Test public void changeVariableValueTest() throws CaptureScreenshotException { setNewVariableValue("NewWorld", "name"); checkNewVariableValueIsPropagatedToBrowser(); } @After public void teardown() { try { new ShellMenu("Run", "Terminate").select(); } catch (CoreLayerException ex) { if (ex.getMessage().contains("Menu item is not enabled")) { // no big deal, there is no execution running } else { throw ex; } } // remove all breakpoints BreakpointsView breakpointsView = new BreakpointsView(); breakpointsView.open(); breakpointsView.removeAllBreakpoints(); } private static void doNotSuspendOnUncaughtExceptions() { WorkbenchPreferenceDialog workbenchPreferenceDialog = new WorkbenchPreferenceDialog(); workbenchPreferenceDialog.open(); JavaDebugPreferencePage javaDebugPreferencePage = new JavaDebugPreferencePage(); workbenchPreferenceDialog.select(javaDebugPreferencePage); javaDebugPreferencePage.setSuspendOnUncaughtExceptions(false); workbenchPreferenceDialog.ok(); } private static void toggleAutoBuild(boolean autoBuild) { ShellMenu autoBuildMenuItem = new ShellMenu("Project", "Build Automatically"); boolean isSelected = autoBuildMenuItem.isSelected(); if (autoBuild && !isSelected) { autoBuildMenuItem.select(); } if (!autoBuild && isSelected) { autoBuildMenuItem.select(); } } private static void cleanAndBuildWorkspace() { new ShellMenu("Project", "Clean...").select(); new DefaultShell("Clean"); new OkButton().click(); new WaitWhile(new JobIsRunning()); } private static void waitForserverAdapterToBeInRightState() { new WaitUntil(new ServerHasState(new ServersView().getServer(serverAdapter.getLabel()), ServerState.DEBUGGING)); new WaitUntil(new ServerHasPublishState(new ServersView().getServer(serverAdapter.getLabel()), ServerPublishState.SYNCHRONIZED)); } private void setupBreakpoint() { // set breakpoint where we need it. ProjectItem helloServiceFile = getHelloServiceFile(); setBreakpointToLineWithText(helloServiceFile, "return \"Hello"); } private void checkNewVariableValueIsPropagatedToBrowser() { clickResume(); BrowserEditor browserEditor = new BrowserEditor("helloworld"); browserEditor.activate(); String text = browserEditor.getText(); assertTrue(text.contains("NewWorld")); } private void clickResume() { new WaitWhile(new AbstractWaitCondition() { @Override public boolean test() { ShellMenu resumeMenu = new ShellMenu("Run", "Resume"); if (resumeMenu.isEnabled()) { resumeMenu.select(); return true; } else { return false; } } }); } private void checkDebugView() { DebugView debugView = new DebugView(); debugView.open(); TreeItem createHelloMessageDebugItem = ensureCorrectFrameIsSelected(debugView); assertTrue(createHelloMessageDebugItem.getText().contains("createHelloMessage")); } private TreeItem ensureCorrectFrameIsSelected(DebugView debugView) { List<TreeItem> items; TreeItem createHelloMessageDebugItem; // get frames of suspended thread. If the desired frame is not present, // try reopening Debug view items = getSuspendedThreadTreeItem(debugView).getItems(); if (items.size() < 2) { // no stack trace available. Try to close&reopen Debug view (dirty // hack) debugView.close(); debugView = new DebugView(); debugView.open(); items = getSuspendedThreadTreeItem(debugView).getItems(); } final List<TreeItem> tIList = items; // wait for frame texts to populate. new WaitUntil(new AbstractWaitCondition() { @Override public boolean test() { return tIList.stream() .peek(ti -> LOGGER.debug(ti.getText())) .filter(ti -> ti.getText().contains("createHelloMessage")) .findFirst() .isPresent(); } }); createHelloMessageDebugItem = tIList.stream() .peek(ti -> LOGGER.debug(ti.getText())) .filter(ti -> ti.getText().contains("createHelloMessage")) .findFirst() .get(); // select the item and return it createHelloMessageDebugItem.select(); return createHelloMessageDebugItem; } private TreeItem getSuspendedThreadTreeItem(DebugView debugView) { // get top item debugView.activate(); DefaultTree parent = new DefaultTree(); TreeItem remoteDebuggerTreeItem = parent.getItems().stream().filter(containsStringPredicate("Remote debugger")) .findFirst().get(); List<TreeItem> items = remoteDebuggerTreeItem.getItems(); TreeItem openJDKTreeItem = items.get(0); // this could (and will) change when run with another JDK - need // investigation assertTrue(openJDKTreeItem.getText().contains("OpenJDK")); // wait until we can see the suspended thread SuspendedTreeItemIsReady suspendedTreeItemIsReady = new SuspendedTreeItemIsReady(openJDKTreeItem); new WaitUntil(suspendedTreeItemIsReady); return suspendedTreeItemIsReady.getSuspendedTreeItem(); } private Predicate<TreeItem> containsStringPredicate(String string) { return treeItem -> treeItem.getText().contains(string); } private void checkVariablesView() { VariablesView variablesView = new VariablesView(); variablesView.open(); // wait for variables to have correct value new WaitUntil(new AbstractWaitCondition() { @Override public boolean test() { return variablesView.getValue("name").equals("World"); } }); new WaitUntil(new AbstractWaitCondition() { @Override public boolean test() { return variablesView.getValue("this").contains("HelloService"); } }); } private void triggerDebugSession() { serverAdapter.select(); new ContextMenu("Show In", "Web Browser").select(); try { new WaitUntil(new BrowserIsReadyElseReloadCondition(serverAdapter)); } catch (WaitTimeoutExpiredException e) { throw e; } new WaitUntil(new AbstractWaitCondition() { @Override public boolean test() { TextEditor currentEditor = null; try { currentEditor = new TextEditor(); } catch (WorkbenchLayerException ex) { // current editor is not TextEditor return false; } if (currentEditor.getTitle().contains("HelloService.java")) { // textEditor is active. return true; } // try to reload again serverAdapter.select(); new ContextMenu("Show In", "Web Browser").select(); return false; } }, TimePeriod.NORMAL, false); } private ProjectItem getHelloServiceFile() { ProjectExplorer projectExplorer = new ProjectExplorer(); projectExplorer.open(); Project project = projectExplorer.getProject(PROJECT_NAME); ProjectItem helloServiceFile = project.getProjectItem("Java Resources", "src/main/java", "org.jboss.as.quickstarts.helloworld", "HelloService.java"); return helloServiceFile; } // Sets breakpoint to first appearance of given text. private void setBreakpointToLineWithText(ProjectItem file, String text) { file.open(); TextEditor textEditor = new TextEditor("HelloService.java"); textEditor.setCursorPosition(textEditor.getPositionOfText(text)); new ShellMenu("Run", "Toggle Breakpoint").select(); } private static void createServerAdapter() { OpenShiftExplorerView explorer = new OpenShiftExplorerView(); explorer.getOpenShift3Connection().getProject().getService("eap-app").createServerAdapter(); } private static void disableShowConsoleWhenOutputChanges() { ConsoleView consoleView = new ConsoleView(); consoleView.open(); new WaitUntil(new ShowConsoleOutputToolItemIsAvailable()); DefaultToolItem showConsoleOnChange = new DefaultToolItem(new WorkbenchShell(), "Show Console Output When Standard Out Changes"); showConsoleOnChange.click(); } // TODO this should be replaced once // https://github.com/jboss-reddeer/reddeer/issues/1668 is fixed. private void setNewVariableValue(String newValue, final String... variablePath) { new WaitWhile(new JobIsRunning()); DebugView debugView = new DebugView(); debugView.open(); ensureCorrectFrameIsSelected(debugView); VariablesView variablesView = new VariablesView(); variablesView.open(); new WaitUntil(new AbstractWaitCondition() { @Override public boolean test() { try { TreeItem variable = new DefaultTreeItem(variablePath); variable.select(); return variable.isSelected(); } catch (Exception e) { return false; } } @Override public String description() { return "Variable is not selected"; } }); try { new ContextMenu("Change Value...").select(); } catch (CoreLayerException e) { throw e; } new DefaultShell("Change Object Value"); new DefaultStyledText().setText(newValue); new OkButton().click(); new WaitWhile(new JobIsRunning()); } private static class ShowConsoleOutputToolItemIsAvailable extends AbstractWaitCondition { @Override public boolean test() { try { new DefaultToolItem(new WorkbenchShell(), "Show Console Output When Standard Out Changes"); return true; } catch (CoreLayerException ex) { return false; } } } }