Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions org.eclipse.jdt.debug.tests/testprograms/StackFrameColoring.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*******************************************************************************
* Copyright (c) 2025 Zsombor Gegesy 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:
* Zsombor Gegesy - initial API and implementation
*******************************************************************************/

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class StackFrameColoring {

public static void main(String[] args) {
new StackFrameColoring().run();
}

void run() {
List<String> result = Arrays.asList("hello", "world").stream().map(value -> {
breakpointMethod();
return value;
}).collect(Collectors.toList());
System.out.println("StackFrameColoring.run called: "+ result);
}

public void breakpointMethod() {
System.out.println("set a breakpoint here");
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ public abstract class AbstractDebugTest extends TestCase implements IEvaluation
public static final String CLONE_SUFFIX = "Clone";

final String[] LAUNCH_CONFIG_NAMES_1_4 = { "LargeSourceFile", "LotsOfFields",
"Breakpoints",
"Breakpoints", "StackFrameColoring",
"InstanceVariablesTests",
"LocalVariablesTests", "LocalVariableTests2", "StaticVariablesTests",
"DropTests", "ThrowsNPE", "ThrowsException", "org.eclipse.debug.tests.targets.Watchpoint",
Expand Down Expand Up @@ -3068,7 +3068,7 @@ protected void assertNoErrorMarkersExist(IProject[] projects) throws Exception {
protected void assertNoErrorMarkersExist(IProject project) throws Exception {
if (project.isAccessible()) {
IMarker[] projectMarkers = project.findMarkers(null, false, IResource.DEPTH_INFINITE);
List<IMarker> errorMarkers = Arrays.stream(projectMarkers).filter(marker -> isErrorMarker(marker)).collect(Collectors.toList());
List<IMarker> errorMarkers = Arrays.stream(projectMarkers).filter(AbstractDebugTest::isErrorMarker).toList();
String projectErrors = toString(errorMarkers);
assertEquals("found errors on project " + project + ":" + System.lineSeparator() + projectErrors, Collections.EMPTY_LIST, errorMarkers);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,8 +149,10 @@
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.GroupedStackFrameTest;
import org.eclipse.jdt.debug.tests.ui.JavaSnippetEditorTest;
import org.eclipse.jdt.debug.tests.ui.OpenFromClipboardTests;
import org.eclipse.jdt.debug.tests.ui.StackFrameGroupingTest;
import org.eclipse.jdt.debug.tests.ui.ViewManagementTests;
import org.eclipse.jdt.debug.tests.ui.VirtualThreadsDebugViewTests;
import org.eclipse.jdt.debug.tests.ui.presentation.ModelPresentationTests;
Expand Down Expand Up @@ -246,6 +248,8 @@ public AutomatedSuite() {
addTest(new TestSuite(StepFilterTests.class));
addTest(new TestSuite(StepIntoSelectionTests.class));
addTest(new TestSuite(InstanceFilterTests.class));
addTest(new TestSuite(StackFrameGroupingTest.class));
addTest(new TestSuite(GroupedStackFrameTest.class));
if (JavaProjectHelper.isJava6Compatible()) {
addTest(new TestSuite(ForceReturnTests.class));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -147,22 +147,22 @@ protected IJavaThread launchToBreakpoint(String typeName, String breakpointMetho
return thread;
}

protected void assertStackFrameIsSelected(String breakpointMethodName) throws Exception {
protected TreeItem assertStackFrameIsSelected(String breakpointMethodName) throws Exception {
// Get and check the selection form the tree, we expect only one method selected
TreeItem[] selected = getSelectedItemsFromDebugView(true);
Object[] selectedText = selectedText(selected);
if (selected.length != 1) {
if (Platform.OS.isMac()) {
// skip this test on Mac - see bug 516024
return;
return null;
}
throw new TestAgainException("Unexpected selection: " + Arrays.toString(selectedText));
}
assertEquals("Unexpected selection: " + Arrays.toString(selectedText), 1, selected.length);
IJavaStackFrame selectedFrame = selectedFrame(selected);

assertEquals("\"breakpointMethod\" should be selected after reaching breakpoint", selectedFrame.getMethodName(), breakpointMethodName);

return selected[0];
}

@Override
Expand Down Expand Up @@ -208,7 +208,7 @@ protected void waitForNonConsoleJobs() throws Exception {
}

protected Object[] selectedText(TreeItem[] selected) throws Exception {
Object[] selectedText = sync(() -> Arrays.stream(selected).map(x -> x.getText()).toArray());
Object[] selectedText = sync(() -> Arrays.stream(selected).map(TreeItem::getText).toArray());
return selectedText;
}

Expand All @@ -235,7 +235,7 @@ protected String dumpFrames(Object[] childrenData) {

protected TreeItem[] getSelectedItemsFromDebugView(boolean wait) throws Exception {
return sync(() -> {
Tree tree = (Tree) debugView.getViewer().getControl();
Tree tree = getDebugViewTree();
TreeItem[] selected = tree.getSelection();
if (!wait) {
return selected;
Expand All @@ -254,6 +254,24 @@ protected TreeItem[] getSelectedItemsFromDebugView(boolean wait) throws Exceptio
});
}

private Tree getDebugViewTree() {
return (Tree) debugView.getViewer().getControl();
}

protected IJavaThread runCodeUntilBreakpoint(String typeName, String breakpointMethodName) throws Exception {
sync(() -> getActivePage().hideView(getActivePage().findView(IDebugUIConstants.ID_DEBUG_VIEW)));

waitForNonConsoleJobs();
assertNoErrorMarkersExist();
setPreferenceToShowSystemThreads();
sync(() -> openEditor(typeName + ".java"));

var thread = launchToBreakpoint(typeName, breakpointMethodName, 1);
assertDebugViewIsOpen();

return thread;
}

protected ISelection getDebugViewSelection() throws Exception {
return debugView.getViewer().getSelection();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,12 @@
import org.eclipse.debug.core.model.IStackFrame;
import org.eclipse.debug.ui.IDebugUIConstants;
import org.eclipse.jdt.debug.core.IJavaStackFrame;
import org.eclipse.jdt.debug.core.IJavaStackFrame.Category;
import org.eclipse.jdt.debug.core.IJavaThread;
import org.eclipse.jdt.debug.ui.IJavaDebugUIConstants;
import org.eclipse.jdt.internal.debug.ui.IJDIPreferencesConstants;
import org.eclipse.jdt.internal.debug.ui.JDIDebugUIPlugin;
import org.eclipse.jdt.internal.debug.ui.StackFrameCategorizer;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.swt.widgets.TreeItem;
import org.eclipse.test.OrderedTestSuite;
Expand Down Expand Up @@ -226,6 +229,42 @@ public void testWrongSelectionBug540243() throws Exception {
doTestWrongSelection(iterations, typeName, breakpointMethodName, expectedBreakpointHitsCount);
}

public void testStackFrameGrouppingAndColors() throws Exception {
IPreferenceStore jdiUIPreferences = JDIDebugUIPlugin.getDefault().getPreferenceStore();
jdiUIPreferences.setValue(IJDIPreferencesConstants.PREF_COLLAPSE_STACK_FRAMES, true);
IJavaThread thread = null;
try {
thread = runCodeUntilBreakpoint("StackFrameColoring", "breakpointMethod");
assertNotNull("thread", thread);
var selectedStackFrame = assertStackFrameIsSelected("breakpointMethod");
if (selectedStackFrame == null) {
// skip this test on Mac - see bug 516024
return;
}
sync(() -> {
var allFrames = selectedStackFrame.getParentItem().getItems();
assertNotNull("all frames", allFrames);
assertEquals("frame[0]", "StackFrameColoring.breakpointMethod() line: 34", allFrames[0].getText());
assertEquals("frame[0] - production", StackFrameCategorizer.CATEGORY_PRODUCTION, getStackFrameCategory(allFrames, 0));
assertEquals("frame[1]", "StackFrameColoring.lambda$0(String) line: 27", allFrames[1].getText());
assertEquals("frame[1] - production", StackFrameCategorizer.CATEGORY_PRODUCTION, getStackFrameCategory(allFrames, 1));
assertTrue("frame[2]", allFrames[2].getText().contains("apply(Object) line: not available"));
assertEquals("frame[2] - production", StackFrameCategorizer.CATEGORY_PRODUCTION, getStackFrameCategory(allFrames, 2));
assertEquals("frame[3]", "7 collapsed frames", allFrames[3].getText());
assertEquals("frame[4]", "StackFrameColoring.run() line: 29", allFrames[4].getText());
assertEquals("frame[4] - production", StackFrameCategorizer.CATEGORY_PRODUCTION, getStackFrameCategory(allFrames, 4));
assertEquals("frame[5]", "StackFrameColoring.main(String[]) line: 22", allFrames[5].getText());
assertEquals("frame[5] - production", StackFrameCategorizer.CATEGORY_PRODUCTION, getStackFrameCategory(allFrames, 5));
});
} finally {
terminateAndCleanUp(thread);
}
}

private Category getStackFrameCategory(TreeItem[] allFrames, int idx) {
return ((IJavaStackFrame) allFrames[idx].getData()).getCategory();
}

/**
* Test for Bug 534319 - Debug View shows wrong information due to threads with short lifetime
*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/*******************************************************************************
* Copyright (c) 2025 Zsombor Gegesy 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:
* Zsombor Gegesy - initial API and implementation
*******************************************************************************/
package org.eclipse.jdt.debug.tests.ui;


import org.eclipse.jdt.debug.core.IJavaStackFrame;
import org.eclipse.jdt.internal.debug.core.model.GroupedStackFrame;

import junit.framework.TestCase;

public class GroupedStackFrameTest extends TestCase {

private GroupedStackFrame groupedStackFrame;

private IJavaStackFrame mockFrame1;
private IJavaStackFrame mockFrame2;

@Override
public void setUp() throws Exception {
groupedStackFrame = new GroupedStackFrame(null);
mockFrame1 = JavaStackFrameMock.createFrame(JavaReferenceTypeMock.createReference("java.util.ArrayList"), false);
mockFrame2 = JavaStackFrameMock.createFrame(JavaReferenceTypeMock.createReference("java.util.LinkedList"), false);
}

public void testAddFrame() {
groupedStackFrame.add(mockFrame1);
assertEquals(1, groupedStackFrame.getFrameCount());

groupedStackFrame.add(mockFrame2);
assertEquals(2, groupedStackFrame.getFrameCount());
}

public void testGetFrameCount() {
assertEquals(0, groupedStackFrame.getFrameCount());

groupedStackFrame.add(mockFrame1);
assertEquals(1, groupedStackFrame.getFrameCount());

groupedStackFrame.add(mockFrame2);
assertEquals(2, groupedStackFrame.getFrameCount());
}

public void testGetFramesAsArray() {
groupedStackFrame.add(mockFrame1);
groupedStackFrame.add(mockFrame2);

Object[] frames = groupedStackFrame.getFramesAsArray(0, 2);
assertNotNull(frames);
assertEquals(2, frames.length);
assertSame(mockFrame1, frames[0]);
assertSame(mockFrame2, frames[1]);

frames = groupedStackFrame.getFramesAsArray(1, 1);
assertNotNull(frames);
assertEquals(1, frames.length);
assertSame(mockFrame2, frames[0]);

frames = groupedStackFrame.getFramesAsArray(2, 1);
assertNull(frames);
}

public void testGetTopMostFrame() {
assertNull(groupedStackFrame.getTopMostFrame());

groupedStackFrame.add(mockFrame1);
assertSame(mockFrame1, groupedStackFrame.getTopMostFrame());

groupedStackFrame.add(mockFrame2);
assertSame(mockFrame1, groupedStackFrame.getTopMostFrame());
}

public void testGetAdapter() {
var adapterType = String.class;

groupedStackFrame.add(mockFrame1);
Object adapter = groupedStackFrame.getAdapter(adapterType);
assertEquals("getAdapter called with class java.lang.String", adapter);

groupedStackFrame = new GroupedStackFrame(null);
adapter = groupedStackFrame.getAdapter(adapterType);
assertNull(adapter);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*******************************************************************************
* Copyright (c) 2025 Zsombor Gegesy 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:
* Zsombor Gegesy - initial API and implementation
*******************************************************************************/
package org.eclipse.jdt.debug.tests.ui;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

import org.eclipse.jdt.debug.core.IJavaReferenceType;

/**
* Class to mock {@link IJavaReferenceType}.
*/
class JavaReferenceTypeMock implements InvocationHandler {

final String name;

JavaReferenceTypeMock(String name) {
this.name = name;
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if ("getName".equals(method.getName())) {
return name;
}
return null;
}

/**
* Create a new mocked {@link IJavaReferenceType}.
*
* @param name
* @return
*/
public static IJavaReferenceType createReference(String name) {
return (IJavaReferenceType) Proxy.newProxyInstance(JavaReferenceTypeMock.class.getClassLoader(), new Class[] {
IJavaReferenceType.class }, new JavaReferenceTypeMock(name));
}

}
Loading
Loading