diff --git a/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/AutomatedSuite.java b/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/AutomatedSuite.java index e2cbcfb1b5..19d44f0d8d 100644 --- a/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/AutomatedSuite.java +++ b/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/AutomatedSuite.java @@ -149,7 +149,9 @@ import org.eclipse.jdt.debug.tests.ui.DebugHoverTests; import org.eclipse.jdt.debug.tests.ui.DebugViewTests; import org.eclipse.jdt.debug.tests.ui.DetailPaneManagerTests; +import org.eclipse.jdt.debug.tests.ui.HotCodeReplaceErrorDialogTest; import org.eclipse.jdt.debug.tests.ui.JavaSnippetEditorTest; +import org.eclipse.jdt.debug.tests.ui.NoLineNumberAttributesStatusHandlerTest; import org.eclipse.jdt.debug.tests.ui.OpenFromClipboardTests; import org.eclipse.jdt.debug.tests.ui.ViewManagementTests; import org.eclipse.jdt.debug.tests.ui.VirtualThreadsDebugViewTests; @@ -341,6 +343,12 @@ public AutomatedSuite() { // Scrapbook editor tests addTest(new TestSuite(JavaSnippetEditorTest.class)); + // No Line Number Attributes Status Handler tests + addTest(new TestSuite(NoLineNumberAttributesStatusHandlerTest.class)); + + // Test that ErrorDialogWithToggle Override functionalities won't cause a Stackoverflow Exception + addTest(new TestSuite(HotCodeReplaceErrorDialogTest.class)); + // Debug hover tests addTest(new TestSuite(DebugHoverTests.class)); diff --git a/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/TestDebugTarget.java b/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/TestDebugTarget.java new file mode 100644 index 0000000000..2aaaf4b1c8 --- /dev/null +++ b/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/TestDebugTarget.java @@ -0,0 +1,154 @@ +/******************************************************************************* + * Copyright (c) 2025 Advantest Corporation and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Advantest Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.jdt.debug.tests; + +import org.eclipse.core.resources.IMarkerDelta; +import org.eclipse.debug.core.DebugException; +import org.eclipse.debug.core.ILaunch; +import org.eclipse.debug.core.model.IBreakpoint; +import org.eclipse.debug.core.model.IDebugTarget; +import org.eclipse.debug.core.model.IMemoryBlock; +import org.eclipse.debug.core.model.IProcess; +import org.eclipse.debug.core.model.IThread; + +/** + * This is a test debug target which can be used to mimic an IDebugTarget instance. + */ +public class TestDebugTarget implements IDebugTarget { + + @Override + public boolean supportsStorageRetrieval() { + return false; + } + + @Override + public IMemoryBlock getMemoryBlock(long startAddress, long length) throws DebugException { + return null; + } + + @Override + public boolean isDisconnected() { + return false; + } + + @Override + public void disconnect() throws DebugException { + + } + + @Override + public boolean canDisconnect() { + return false; + } + + @Override + public void breakpointRemoved(IBreakpoint breakpoint, IMarkerDelta delta) { + + } + + @Override + public void breakpointChanged(IBreakpoint breakpoint, IMarkerDelta delta) { + + } + + @Override + public void breakpointAdded(IBreakpoint breakpoint) { + + } + + @Override + public void suspend() throws DebugException { + + } + + @Override + public void resume() throws DebugException { + + } + + @Override + public boolean isSuspended() { + return false; + } + + @Override + public boolean canSuspend() { + return false; + } + + @Override + public boolean canResume() { + return false; + } + + @Override + public void terminate() throws DebugException { + + } + + @Override + public boolean isTerminated() { + return false; + } + + @Override + public boolean canTerminate() { + return false; + } + + @Override + public T getAdapter(Class adapter) { + return null; + } + + @Override + public String getModelIdentifier() { + return null; + } + + @Override + public ILaunch getLaunch() { + return null; + } + + @Override + public IDebugTarget getDebugTarget() { + return null; + } + + @Override + public boolean supportsBreakpoint(IBreakpoint breakpoint) { + return false; + } + + @Override + public boolean hasThreads() throws DebugException { + return false; + } + + @Override + public IThread[] getThreads() throws DebugException { + return null; + } + + @Override + public IProcess getProcess() { + return null; + } + + @Override + public String getName() throws DebugException { + return null; + } +} \ No newline at end of file diff --git a/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/ui/HotCodeReplaceErrorDialogTest.java b/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/ui/HotCodeReplaceErrorDialogTest.java new file mode 100644 index 0000000000..8a166a6fdf --- /dev/null +++ b/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/ui/HotCodeReplaceErrorDialogTest.java @@ -0,0 +1,111 @@ +/******************************************************************************* + * Copyright (c) 2025 Advantest Corporation and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Advantest Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.jdt.debug.tests.ui; + +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.debug.core.model.IDebugTarget; +import org.eclipse.jdt.debug.tests.TestDebugTarget; +import org.eclipse.jdt.internal.debug.ui.DebugUIMessages; +import org.eclipse.jdt.internal.debug.ui.HotCodeReplaceErrorDialog; +import org.eclipse.jdt.internal.debug.ui.IJDIPreferencesConstants; +import org.eclipse.jdt.internal.debug.ui.JDIDebugUIPlugin; +import org.eclipse.jface.dialogs.ErrorDialog; +import org.eclipse.jface.dialogs.IDialogConstants; +import org.eclipse.jface.preference.IPreferenceStore; +import org.eclipse.osgi.util.NLS; +import org.eclipse.swt.events.ShellAdapter; +import org.eclipse.swt.events.ShellEvent; +import org.eclipse.swt.widgets.Shell; +import org.junit.Test; + +public class HotCodeReplaceErrorDialogTest extends AbstractDebugUiTests { + + private final class HotCodeReplaceErrorDialogSimRunnable implements Runnable { + private final IStatus status; + private final String toggleMessage; + private final String prefAlertObsoleteMethods; + private final String toggleMessage2; + private final IPreferenceStore preferenceStore; + private final String message; + private final String dialogTitle; + private final Shell shell; + + private HotCodeReplaceErrorDialogSimRunnable(IStatus status, String toggleMessage, String prefAlertObsoleteMethods, String toggleMessage2, IPreferenceStore preferenceStore, String message, String dialogTitle, Shell shell) { + this.status = status; + this.toggleMessage = toggleMessage; + this.prefAlertObsoleteMethods = prefAlertObsoleteMethods; + this.toggleMessage2 = toggleMessage2; + this.preferenceStore = preferenceStore; + this.message = message; + this.dialogTitle = dialogTitle; + this.shell = shell; + } + + @Override + public void run() { + class HotCodeReplaceErrorDialogExtension extends HotCodeReplaceErrorDialog { + private HotCodeReplaceErrorDialogExtension(Shell parentShell, String dialogTitle, String message, IStatus status, String preferenceKey, String toggleMessage, String toggleMessage2, IPreferenceStore store, IDebugTarget target) { + super(parentShell, dialogTitle, message, status, preferenceKey, toggleMessage, toggleMessage2, store, target); + } + + @Override + public void buttonPressed(int id) { + super.buttonPressed(id); + } + } + + HotCodeReplaceErrorDialogExtension errorDialog = new HotCodeReplaceErrorDialogExtension(shell, dialogTitle, message, status, prefAlertObsoleteMethods, toggleMessage, toggleMessage2, preferenceStore, new TestDebugTarget()); + final boolean originalMode = ErrorDialog.AUTOMATED_MODE; + ErrorDialog.AUTOMATED_MODE = false; + try { + errorDialog.create(); + errorDialog.getShell().addShellListener(new ShellAdapter() { + @Override + public void shellActivated(ShellEvent e) { + // To see dialog: processUiEvents(500); + processUiEvents(); + errorDialog.buttonPressed(IDialogConstants.OK_ID); + } + }); + errorDialog.open(); + } catch (Exception e) { + throw new RuntimeException(e); + } finally { + ErrorDialog.AUTOMATED_MODE = originalMode; + errorDialog.close(); + } + } + } + + public HotCodeReplaceErrorDialogTest(String name) { + super(name); + } + + @Test + public void testHotCodeReplaceErrorDialog() { + Shell shell = JDIDebugUIPlugin.getActiveWorkbenchShell(); + final String vmName = "Dummy VM"; + final String dialogTitle = DebugUIMessages.JDIDebugUIPlugin_Obsolete_methods_remain_1; + final String message = NLS.bind(DebugUIMessages.JDIDebugUIPlugin__0__contains_obsolete_methods_1, new Object[] { vmName }); + final IStatus status = new Status(IStatus.WARNING, JDIDebugUIPlugin.getUniqueIdentifier(), IStatus.WARNING, DebugUIMessages.JDIDebugUIPlugin_Stepping_may_be_hazardous_1, null); + final String toggleMessage = DebugUIMessages.JDIDebugUIPlugin_2; + final String toggleMessage2 = DebugUIMessages.JDIDebugUIPlugin_5; + IPreferenceStore preferenceStore = JDIDebugUIPlugin.getDefault().getPreferenceStore(); + String prefAlertObsoleteMethods = IJDIPreferencesConstants.PREF_ALERT_OBSOLETE_METHODS; + + Runnable dialogSimRunnable = new HotCodeReplaceErrorDialogSimRunnable(status, toggleMessage, prefAlertObsoleteMethods, toggleMessage2, preferenceStore, message, dialogTitle, shell); + sync(dialogSimRunnable); + } +} diff --git a/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/ui/NoLineNumberAttributesStatusHandlerTest.java b/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/ui/NoLineNumberAttributesStatusHandlerTest.java new file mode 100644 index 0000000000..4578f8ea07 --- /dev/null +++ b/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/ui/NoLineNumberAttributesStatusHandlerTest.java @@ -0,0 +1,188 @@ +/******************************************************************************* + * Copyright (c) 2025 Advantest Corporation and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Advantest Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.jdt.debug.tests.ui; + +import static org.eclipse.jdt.internal.debug.ui.IJDIPreferencesConstants.PREF_ALERT_UNABLE_TO_INSTALL_BREAKPOINT; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.eclipse.core.runtime.ILogListener; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.Status; +import org.eclipse.debug.core.DebugPlugin; +import org.eclipse.jdi.internal.ClassTypeImpl; +import org.eclipse.jdi.internal.ReferenceTypeImpl; +import org.eclipse.jdt.internal.debug.core.breakpoints.JavaLineBreakpoint; +import org.eclipse.jdt.internal.debug.ui.DebugUIMessages; +import org.eclipse.jdt.internal.debug.ui.ErrorDialogWithToggle; +import org.eclipse.jdt.internal.debug.ui.JDIDebugUIPlugin; +import org.eclipse.jdt.internal.debug.ui.NoLineNumberAttributesStatusHandler; +import org.eclipse.jface.dialogs.ErrorDialog; +import org.eclipse.jface.dialogs.IDialogConstants; +import org.eclipse.jface.preference.IPreferenceStore; +import org.eclipse.osgi.util.NLS; +import org.eclipse.swt.events.ShellAdapter; +import org.eclipse.swt.events.ShellEvent; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.test.OrderedTestSuite; +import org.eclipse.ui.PlatformUI; +import org.junit.Test; + +/** + * This test is checking the {@link NoLineNumberAttributesStatusHandler} the functionality that the ok button click is saving the state in the + * preference store. + */ +public class NoLineNumberAttributesStatusHandlerTest extends AbstractDebugUiTests { + + public static junit.framework.Test suite() { + return new OrderedTestSuite(NoLineNumberAttributesStatusHandlerTest.class); + } + + private static final class ErrorDialogWithToggleRunnable implements Runnable { + + private final IPreferenceStore preferenceStore; + private final IStatus status; + private final boolean toggleValue; + + private ErrorDialogWithToggleRunnable(IPreferenceStore preferenceStore, IStatus status, boolean toggleValue) { + this.preferenceStore = preferenceStore; + this.status = status; + this.toggleValue = toggleValue; + } + + @Override + public void run() { + Shell shell = PlatformUI.getWorkbench().getModalDialogShellProvider().getShell(); + class ErrorDialogWithToggleForTest extends ErrorDialogWithToggle { + + public ErrorDialogWithToggleForTest(Shell parentShell, String dialogTitle, String message, IStatus status, String preferenceKey, String toggleMessage, IPreferenceStore store) { + super(parentShell, dialogTitle, message, status, preferenceKey, toggleMessage, store); + } + + /** + * Overridden to make public. + */ + @Override + public Button getToggleButton() { + return super.getToggleButton(); + } + + /** + * Overridden to make public. + */ + @Override + public void buttonPressed(int id) { + super.buttonPressed(id); + } + } + ErrorDialogWithToggleForTest dialog = new ErrorDialogWithToggleForTest(shell, DebugUIMessages.NoLineNumberAttributesStatusHandler_Java_Breakpoint_1, NLS.bind(DebugUIMessages.NoLineNumberAttributesStatusHandler_2, "HelloWorld"), status, PREF_ALERT_UNABLE_TO_INSTALL_BREAKPOINT, DebugUIMessages.NoLineNumberAttributesStatusHandler_3, preferenceStore); + final boolean originalMode = ErrorDialog.AUTOMATED_MODE; + ErrorDialog.AUTOMATED_MODE = false; + try { + dialog.create(); + dialog.getToggleButton().setSelection(toggleValue); + dialog.getShell().addShellListener(new ShellAdapter() { + @Override + public void shellActivated(ShellEvent e) { + // To see dialog: processUiEvents(500); + processUiEvents(); + dialog.buttonPressed(IDialogConstants.OK_ID); + } + }); + dialog.open(); + } catch (Exception e) { + throw new RuntimeException(e); + } finally { + ErrorDialog.AUTOMATED_MODE = originalMode; + dialog.close(); + } + } + + } + + private static final class ListLogListener implements ILogListener { + private final List loggedEntries; + + private ListLogListener(List loggedEntries) { + this.loggedEntries = loggedEntries; + } + + @Override + public void logging(IStatus status, String plugin) { + if (status.isMultiStatus() && status.getChildren().length == 1) { + loggedEntries.add(status.getChildren()[0]); + } + } + } + + public NoLineNumberAttributesStatusHandlerTest(String name) { + super(name); + } + + @Test + public void testPreferenceSettings() throws Exception { + List loggedEntries = new ArrayList<>(); + ILogListener listener = new ListLogListener(loggedEntries); + + Platform.addLogListener(listener); + try { + IPreferenceStore preferenceStore = JDIDebugUIPlugin.getDefault().getPreferenceStore(); + + IStatus status = new Status(IStatus.ERROR, "org.eclipse.jdt.debug", JavaLineBreakpoint.NO_LINE_NUMBERS, "Teststatus", null); + + // No errors should be logged after "don't tell me" preference is set + boolean dontTellMeAgain = true; + simulateErrorDialogWithToggleExecution(preferenceStore, status, dontTellMeAgain); + assertFalse("Wrong preference set for alert", preferenceStore.getBoolean(PREF_ALERT_UNABLE_TO_INSTALL_BREAKPOINT)); + assertTrue("Expected no logged entries but got: " + loggedEntries, loggedEntries.isEmpty()); + triggerNoLineAttributesStatusHandler(status); + assertEquals("Expected no logged entries but got: " + loggedEntries, 0, Collections.frequency(loggedEntries, status)); + + // Error should be logged if "don't tell me" preference is not set + dontTellMeAgain = false; + simulateErrorDialogWithToggleExecution(preferenceStore, status, dontTellMeAgain); + assertTrue("Wrong preference set for alert", preferenceStore.getBoolean(PREF_ALERT_UNABLE_TO_INSTALL_BREAKPOINT)); + assertTrue("Expected no logged entries but got: " + loggedEntries, loggedEntries.isEmpty()); + triggerNoLineAttributesStatusHandler(status); + assertEquals(1, Collections.frequency(loggedEntries, status)); + loggedEntries.clear(); + + // No errors should be logged after "don't tell me" preference is set again + dontTellMeAgain = true; + simulateErrorDialogWithToggleExecution(preferenceStore, status, dontTellMeAgain); + assertFalse("Wrong preference set for alert", preferenceStore.getBoolean(PREF_ALERT_UNABLE_TO_INSTALL_BREAKPOINT)); + assertTrue("Expected no logged entries but got: " + loggedEntries, loggedEntries.isEmpty()); + triggerNoLineAttributesStatusHandler(status); + assertEquals("Expected no logged entries but got: " + loggedEntries, 0, Collections.frequency(loggedEntries, status)); + } finally { + Platform.removeLogListener(listener); + } + } + + private void triggerNoLineAttributesStatusHandler(IStatus status) { + ReferenceTypeImpl referenceTypeImpl = new ClassTypeImpl(null, null); + referenceTypeImpl.setName("TestRefTypeName"); + NoLineNumberAttributesStatusHandler statusHandler = (NoLineNumberAttributesStatusHandler) DebugPlugin.getDefault().getStatusHandler(status); + statusHandler.handleStatus(status, referenceTypeImpl); + } + + private void simulateErrorDialogWithToggleExecution(IPreferenceStore preferenceStore, IStatus status, boolean toggleValue) throws Exception { + ErrorDialogWithToggleRunnable runnable = new ErrorDialogWithToggleRunnable(preferenceStore, status, toggleValue); + sync(runnable); + } +} diff --git a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/ErrorDialogWithToggle.java b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/ErrorDialogWithToggle.java index b108fa7f36..4cfee8f5e6 100644 --- a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/ErrorDialogWithToggle.java +++ b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/ErrorDialogWithToggle.java @@ -105,11 +105,17 @@ protected void buttonPressed(int id, IDebugTarget target) { if (id == IDialogConstants.OK_ID) { // was the OK button pressed? storePreference(target); } + } + + @Override + protected void buttonPressed(int id) { + if (id == IDialogConstants.OK_ID) { // was the OK button pressed? + fStore.setValue(fPreferenceKey, !getToggleButton().getSelection()); + } super.buttonPressed(id); } private void storePreference(IDebugTarget target) { - fStore.setValue(fPreferenceKey, !getToggleButton().getSelection()); if (fToggleButton2 != null) { if (target instanceof JDIDebugTarget jdiTarget) { jdiTarget.setHcrDebugErrorPref(fToggleButton2.getSelection()); diff --git a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/HotCodeReplaceErrorDialog.java b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/HotCodeReplaceErrorDialog.java index 08f67bddef..20d8f876dc 100644 --- a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/HotCodeReplaceErrorDialog.java +++ b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/HotCodeReplaceErrorDialog.java @@ -153,6 +153,7 @@ public void run() { okPressed(); } else { super.buttonPressed(id, target); + super.buttonPressed(id); } }