Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Drop to frame feature disabled in Eclipse Debug while running testcases using JUnit

Environment: Linux, Eclipse Juno, Java 7, JUnit

When a simple application (a java class with main method) is run in debug mode, 'Drop to Frame' feature works fine in Eclipse. However if the same method is invoked from a junit test case, 'Drop to Frame' feature is disabled in Eclipse. From the documentation

Note this command is only available if the current VM supports drop to frame and the selected stackframe is not the top frame or a frame in a native method.

As we can see from the stack frames in Debug window when a junit test case is run, there is a frame 'NativeMethodAccessorImpl.invoke' which is native. I'm assuming this is the reason for 'Drop to Frame' being disabled.

Let me know if this reasoning is correct and if yes, any workarounds available to overcome this.

like image 803
Ramesh Avatar asked Nov 20 '12 11:11

Ramesh


3 Answers

I use Eclipse Luna, Java 7 under Windows. The situation is still as described: "Drop to frame" is disabled for the test method which immediately follows the 'NativeMethodAccessorImpl.invoke' frame. The disabled state of the "Drop to frame" is bound to method canDropToFrame() respective supportsDropToFrame() in class org.eclipse.jdt.internal.debug.core.model.JDIStackFrame, (in my distribution) part of plugins/org.eclipse.jdt.debug_3.8.102.v20150115-1323/jdimodel.jar. Method supportsDropToFrame() checks if a specific frame can be dropped, and tests

  1. the JVM must support to drop frames
  2. the frame must not be the top most frame
  3. the frame must not be native
  4. the prior frame must not native

So the assumption of Ramesh was right. This is the original code snippet for tests 3 + 4:

int index = 0;
JDIStackFrame frame = null;
while (index < frames.size()) {
    frame = (JDIStackFrame) frames.get(index);
    index++;
    if (frame.isNative()) {
        return false;
    }
    if (frame.equals(this)) {
        if (jdkSupport) {
            // JDK 1.4 VMs are currently unable to pop the
            // frame directly above a native frame
            if (index < frames.size()
                    && ((JDIStackFrame) frames.get(index))
                            .isNative()) {
                return false;
            }
        }
        return true;
    }
}

The comment suggests that it was written in JDK 1.4 times, so maybe in the meantime the JVM can now also drop frames above native frames.

I created a patched version of JDIStackFrame, which skips test 4. Now when pausing in a Junit test method, "Drop to frame" was enabled, as expected.

But when actually dropping the frame, I received an error message box which stated "com.sun.jdi.InternalException: Got error code in reply: 32 occurred popping stack frame".

I assume that this is a JDWP error code. Therefore it seems that such a "Drop to frame" does not work in JDK 1.7 (don't know about 1.8), and it's not a Eclipse thing.

like image 167
wero Avatar answered Oct 18 '22 14:10

wero


This is an old one, but for the time being this is still an issue with Eclipse 2018-09, Java 1.8 and testng as test runner. The workaround (simple and obvious) is to extract the contents of the test into another method, debug it and then inline it back. E.g.:

@Test
public void test() {
    // test goes here
    assertTrue(true);
}

One may use refactoring shortcuts to speed up things: select the body of the test, press Alt+Shift+M, enter name 'inner', and the result is:

@Test
public void test() {
    inner();
}

private void inner() {
    // test goes here and 'drop-to-frame' works well
    assertTrue(true);
}

When done debugging, inline it back by pressing Alt+Shift+I while inside inner().

like image 39
Alex Avatar answered Oct 18 '22 16:10

Alex


I've always dealt with this problem by splitting between the @Test function and a thunk that I can drop frames into.

Pre JDK-8, I'd do this:

@Test
public void testSomeFooInBar() {
    drop_to_frame_testSomeFooInBar();
}

private void drop_to_frame_testSomeFooInBar() {
    assertTrue(somethingOrWhatever);
}

Even though it's verbose, I insist/insisted in calling my thunks after the test functions, with the name indicating what they are for (to "jump/drop to frame"). This is always necessary because there's always someone who does "refactoring" without reading comments and start removing the thunks as "unnecessary."

With JDK 8 and above, I do this:

@Test
public void testSomeFooInBar() {
    final Runnable drop_to_frame = () -> {
        assertTrue(somethingOrWhatever);
    };
    jump_to_frame.run();
}

Simpler. Eclipse will allow you to set a breakpoint inside the runnable lambda and drop to frame as many times as you needed (assuming your logic is sufficiently re-entrant.)

like image 39
luis.espinal Avatar answered Oct 18 '22 15:10

luis.espinal