/*
* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.oracle.truffle.api.debug.test;
import static org.junit.Assert.fail;
import org.junit.Assert;
import org.junit.Test;
import com.oracle.truffle.api.debug.Breakpoint;
import com.oracle.truffle.api.debug.DebuggerSession;
import com.oracle.truffle.api.debug.SuspendedEvent;
import com.oracle.truffle.api.source.Source;
public class StepTest extends AbstractDebugTest {
@Test
public void testBlock() throws Throwable {
final Source source = testSource("ROOT(\n" +
" STATEMENT,\n" +
" STATEMENT,\n" +
" STATEMENT,\n" +
" STATEMENT,\n" +
" STATEMENT,\n" +
" STATEMENT,\n" +
" STATEMENT,\n" +
" STATEMENT\n" +
")\n");
try (DebuggerSession session = startSession()) {
startEval(source);
// make javac happy and use the sessiononce.
session.getDebugger();
expectDone();
}
}
@Test
public void testBlockStepIntoOver() throws Throwable {
final Source source = testSource("ROOT(\n" +
" STATEMENT,\n" +
" STATEMENT,\n" +
" STATEMENT,\n" +
" STATEMENT,\n" +
" STATEMENT,\n" +
" STATEMENT,\n" +
" STATEMENT,\n" +
" STATEMENT\n" +
")\n");
try (DebuggerSession session = startSession()) {
startEval(source);
session.suspendNextExecution();
expectSuspended((SuspendedEvent event) -> {
checkState(event, 2, true, "STATEMENT").prepareStepInto(1);
});
expectSuspended((SuspendedEvent event) -> {
checkState(event, 3, true, "STATEMENT").prepareStepInto(2);
});
expectSuspended((SuspendedEvent event) -> {
checkState(event, 5, true, "STATEMENT").prepareStepOver(1);
});
expectSuspended((SuspendedEvent event) -> {
checkState(event, 6, true, "STATEMENT").prepareStepOver(3);
});
expectSuspended((SuspendedEvent event) -> {
checkState(event, 9, true, "STATEMENT").prepareContinue();
});
expectDone();
}
}
@Test
public void testStepBadArgStepInto() throws Throwable {
final Source source = testSource("ROOT(\n" +
" STATEMENT\n" +
")\n");
try (DebuggerSession session = startSession()) {
startEval(source);
session.suspendNextExecution();
expectSuspended((SuspendedEvent event) -> {
try {
checkState(event, 2, true, "STATEMENT").prepareStepInto(0);
fail("Exception expected");
} catch (IllegalArgumentException e) {
Assert.assertEquals("stepCount must be > 0", e.getMessage());
}
});
expectDone();
}
}
@Test
public void testStepBadArgStepOver() throws Throwable {
final Source source = testSource("ROOT(\n" +
" STATEMENT\n" +
")\n");
try (DebuggerSession session = startSession()) {
startEval(source);
session.suspendNextExecution();
expectSuspended((SuspendedEvent event) -> {
try {
checkState(event, 2, true, "STATEMENT").prepareStepOver(0);
fail("Exception expected");
} catch (IllegalArgumentException e) {
Assert.assertEquals("stepCount must be > 0", e.getMessage());
}
});
expectDone();
}
}
@Test
public void testCallLoopStepInto() throws Throwable {
final Source source = testSource("ROOT(\n" +
" DEFINE(foo,\n" +
" LOOP(3,\n" +
" STATEMENT)\n" +
" ),\n" +
" CALL(foo)\n" +
")\n");
try (DebuggerSession session = startSession()) {
startEval(source);
session.suspendNextExecution();
expectSuspended((SuspendedEvent event) -> {
checkState(event, 4, true, "STATEMENT").prepareStepInto(1);
});
expectSuspended((SuspendedEvent event) -> {
checkState(event, 4, true, "STATEMENT").prepareStepInto(1);
});
expectSuspended((SuspendedEvent event) -> {
checkState(event, 4, true, "STATEMENT").prepareStepInto(1);
});
expectSuspended((SuspendedEvent event) -> {
checkState(event, 6, false, "CALL(foo)").prepareContinue();
});
expectDone();
}
}
@Test
public void testCallLoopStepOut() throws Throwable {
final Source source = testSource("ROOT(\n" +
" DEFINE(foo,\n" +
" LOOP(3,\n" +
" STATEMENT)\n" +
" ),\n" +
" CALL(foo)\n" +
")\n");
try (DebuggerSession session = startSession()) {
startEval(source);
session.suspendNextExecution();
expectSuspended((SuspendedEvent event) -> {
checkState(event, 4, true, "STATEMENT").prepareStepOut(1);
});
expectSuspended((SuspendedEvent event) -> {
checkState(event, 6, false, "CALL(foo)").prepareContinue();
});
expectDone();
}
}
@Test
public void testStepOver1() throws Throwable {
final Source source = testSource("ROOT(\n" +
" DEFINE(bar, STATEMENT),\n" +
" DEFINE(foo, ROOT(STATEMENT(CALL(bar)), \n" +
" STATEMENT(CALL(bar)))),\n" +
" STATEMENT(CALL(foo)),\n" +
" STATEMENT(CALL(foo)),\n" +
" STATEMENT(CALL(foo))\n" +
")\n");
try (DebuggerSession session = startSession()) {
startEval(source);
session.suspendNextExecution();
expectSuspended((SuspendedEvent event) -> {
checkState(event, 5, true, "STATEMENT(CALL(foo))").prepareStepOver(1);
});
expectSuspended((SuspendedEvent event) -> {
checkState(event, 6, true, "STATEMENT(CALL(foo))").prepareStepOver(1);
});
expectSuspended((SuspendedEvent event) -> {
checkState(event, 7, true, "STATEMENT(CALL(foo))").prepareStepOver(1);
});
expectDone();
}
}
@Test
public void testStepIntoAndOut() throws Throwable {
final Source source = testSource("ROOT(\n" +
" DEFINE(bar, STATEMENT),\n" +
" DEFINE(foo, ROOT(STATEMENT(CALL(bar)), \n" +
" STATEMENT(CALL(bar)))),\n" +
" STATEMENT(CALL(foo)),\n" +
" STATEMENT(CALL(foo)),\n" +
" STATEMENT(CALL(foo))\n" +
")\n");
try (DebuggerSession session = startSession()) {
startEval(source);
session.suspendNextExecution();
expectSuspended((SuspendedEvent event) -> {
checkState(event, 5, true, "STATEMENT(CALL(foo))").prepareStepInto(1);
});
expectSuspended((SuspendedEvent event) -> {
checkState(event, 3, true, "STATEMENT(CALL(bar))").prepareStepOut(1);
});
expectSuspended((SuspendedEvent event) -> {
checkState(event, 5, false, "CALL(foo)").prepareStepOver(1);
});
expectSuspended((SuspendedEvent event) -> {
checkState(event, 6, true, "STATEMENT(CALL(foo))").prepareStepInto(1);
});
expectSuspended((SuspendedEvent event) -> {
checkState(event, 3, true, "STATEMENT(CALL(bar))").prepareStepInto(1);
});
expectSuspended((SuspendedEvent event) -> {
checkState(event, 2, true, "STATEMENT").prepareStepOut(1);
});
expectSuspended((SuspendedEvent event) -> {
checkState(event, 3, false, "CALL(bar)").prepareStepOut(1);
});
expectSuspended((SuspendedEvent event) -> {
checkState(event, 6, false, "CALL(foo)").prepareStepOut(1);
});
expectDone();
}
}
@Test
public void testStepInto() throws Throwable {
final Source source = testSource("ROOT(\n" +
" DEFINE(bar, STATEMENT),\n" +
" DEFINE(foo, ROOT(STATEMENT(CALL(bar)), \n" +
" STATEMENT(CALL(bar)))),\n" +
" STATEMENT(CALL(foo)),\n" +
" STATEMENT(CALL(foo))\n" +
")\n");
try (DebuggerSession session = startSession()) {
session.suspendNextExecution();
startEval(source);
expectSuspended((SuspendedEvent event) -> {
checkState(event, 5, true, "STATEMENT(CALL(foo))").prepareStepInto(1);
});
expectSuspended((SuspendedEvent event) -> {
checkState(event, 3, true, "STATEMENT(CALL(bar))").prepareStepInto(1);
});
expectSuspended((SuspendedEvent event) -> {
checkState(event, 2, true, "STATEMENT").prepareStepInto(1);
});
expectSuspended((SuspendedEvent event) -> {
checkState(event, 3, false, "CALL(bar)").prepareStepInto(1);
});
expectSuspended((SuspendedEvent event) -> {
checkState(event, 4, true, "STATEMENT(CALL(bar))").prepareStepInto(1);
});
expectSuspended((SuspendedEvent event) -> {
checkState(event, 2, true, "STATEMENT").prepareStepInto(1);
});
expectSuspended((SuspendedEvent event) -> {
checkState(event, 4, false, "CALL(bar)").prepareStepInto(1);
});
expectSuspended((SuspendedEvent event) -> {
checkState(event, 5, false, "CALL(foo)").prepareStepInto(1);
});
expectSuspended((SuspendedEvent event) -> {
checkState(event, 6, true, "STATEMENT(CALL(foo))").prepareContinue();
});
expectDone();
}
}
@Test
public void testMultipleActions() throws Throwable {
final Source source = testSource("ROOT(\n" + // 1
" DEFINE(bar, STATEMENT),\n" +
" DEFINE(foo, ROOT(STATEMENT(CALL(bar)), \n" +
" STATEMENT(CALL(loop)))),\n" +
" DEFINE(loop,\n" + // 5
" LOOP(3,\n" +
" STATEMENT),\n" +
" STATEMENT\n" +
" ),\n" +
" STATEMENT(CALL(foo)),\n" + // 10
" STATEMENT(CALL(foo)),\n" +
" STATEMENT,\n" +
" STATEMENT(CALL(loop)),\n" +
" STATEMENT,\n" +
" STATEMENT,\n" + // 15
" STATEMENT(CALL(loop)),\n" +
" STATEMENT\n" +
")\n");
try (DebuggerSession session = startSession()) {
session.suspendNextExecution();
Breakpoint bp14 = Breakpoint.newBuilder(source).lineIs(14).build();
Breakpoint bp17 = Breakpoint.newBuilder(source).lineIs(17).build();
session.install(bp14);
session.install(bp17);
startEval(source);
expectSuspended((SuspendedEvent event) -> {
checkState(event, 10, true, "STATEMENT(CALL(foo))").prepareStepInto(1).prepareStepInto(1);
});
expectSuspended((SuspendedEvent event) -> {
checkState(event, 2, true, "STATEMENT").prepareStepOut(1).prepareStepInto(2).prepareStepOver(3);
});
expectSuspended((SuspendedEvent event) -> {
checkState(event, 8, true, "STATEMENT").prepareStepOut(2).prepareStepInto(3);
});
expectSuspended((SuspendedEvent event) -> {
checkState(event, 2, true, "STATEMENT").prepareStepOver(1).prepareStepInto(2);
});
expectSuspended((SuspendedEvent event) -> {
checkState(event, 7, true, "STATEMENT").prepareStepOver(3);
});
expectSuspended((SuspendedEvent event) -> {
checkState(event, 8, true, "STATEMENT").prepareStepOut(2).prepareStepOver(1);
});
expectSuspended((SuspendedEvent event) -> {
checkState(event, 12, true, "STATEMENT").prepareStepOver(1).prepareContinue();
});
// Breakpoint is hit
expectSuspended((SuspendedEvent event) -> {
checkState(event, 14, true, "STATEMENT").prepareStepInto(5).prepareKill();
});
// Breakpoint on line 17 not hit because of the kill
expectKilled();
Assert.assertEquals(1, bp14.getHitCount());
Assert.assertEquals(0, bp17.getHitCount());
}
}
@Test
public void testNoPreparesAfterContinueOrKill() throws Throwable {
final Source source = testSource("ROOT(\n" +
" DEFINE(loop,\n" +
" LOOP(3,\n" +
" STATEMENT),\n" +
" STATEMENT\n" +
" ),\n" +
" STATEMENT(CALL(loop))\n" +
")\n");
try (DebuggerSession session = startSession()) {
session.suspendNextExecution();
Breakpoint bp5 = Breakpoint.newBuilder(source).lineIs(5).build();
session.install(bp5);
startEval(source);
expectSuspended((SuspendedEvent event) -> {
checkState(event, 7, true, "STATEMENT(CALL(loop))").prepareContinue();
try {
event.prepareStepInto(1);
Assert.fail("IllegalStateException should have been thrown.");
} catch (IllegalStateException ex) {
// expected
}
try {
event.prepareStepOver(1);
Assert.fail("IllegalStateException should have been thrown.");
} catch (IllegalStateException ex) {
// expected
}
try {
event.prepareStepOut(1);
Assert.fail("IllegalStateException should have been thrown.");
} catch (IllegalStateException ex) {
// expected
}
try {
event.prepareContinue();
Assert.fail("IllegalStateException should have been thrown.");
} catch (IllegalStateException ex) {
// expected
}
try {
event.prepareKill();
Assert.fail("IllegalStateException should have been thrown.");
} catch (IllegalStateException ex) {
// expected
}
});
expectSuspended((SuspendedEvent event) -> {
checkState(event, 5, true, "STATEMENT").prepareKill();
try {
event.prepareStepInto(1);
Assert.fail("IllegalStateException should have been thrown.");
} catch (IllegalStateException ex) {
// expected
}
try {
event.prepareStepOver(1);
Assert.fail("IllegalStateException should have been thrown.");
} catch (IllegalStateException ex) {
// expected
}
try {
event.prepareStepOut(1);
Assert.fail("IllegalStateException should have been thrown.");
} catch (IllegalStateException ex) {
// expected
}
try {
event.prepareContinue();
Assert.fail("IllegalStateException should have been thrown.");
} catch (IllegalStateException ex) {
// expected
}
try {
event.prepareKill();
Assert.fail("IllegalStateException should have been thrown.");
} catch (IllegalStateException ex) {
// expected
}
});
expectKilled();
}
}
}