Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: jruby/jruby
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: d2150f60038e
Choose a base ref
...
head repository: jruby/jruby
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: a24a6db9734b
Choose a head ref
  • 2 commits
  • 1 file changed
  • 1 contributor

Commits on Nov 24, 2016

  1. Revert "[Truffle] Kill RubyDebugTest - it's testing deprecated APIs."

    Going to update the test.
    This reverts commit 3e64ad9.
    entlicher committed Nov 24, 2016
    Copy the full SHA
    46f756d View commit details
  2. Copy the full SHA
    a24a6db View commit details
Showing with 254 additions and 0 deletions.
  1. +254 −0 truffle/src/test/java/org/jruby/truffle/tck/RubyDebugTest.java
254 changes: 254 additions & 0 deletions truffle/src/test/java/org/jruby/truffle/tck/RubyDebugTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,254 @@
/*
* Copyright (c) 2015, 2016 Oracle and/or its affiliates. All rights reserved. This
* code is released under a tri EPL/GPL/LGPL license. You can use it,
* redistribute it and/or modify it under the terms of the:
*
* Eclipse Public License version 1.0
* GNU General Public License version 2
* GNU Lesser General Public License version 2.1
*/
package org.jruby.truffle.tck;

import com.oracle.truffle.api.debug.Breakpoint;
import com.oracle.truffle.api.debug.DebugStackFrame;
import com.oracle.truffle.api.debug.DebugValue;
import com.oracle.truffle.api.debug.Debugger;
import com.oracle.truffle.api.debug.DebuggerSession;
import com.oracle.truffle.api.debug.SuspendedEvent;
import com.oracle.truffle.api.source.Source;
import com.oracle.truffle.api.vm.PolyglotEngine;
import com.oracle.truffle.api.vm.PolyglotEngine.Value;
import org.jruby.truffle.RubyLanguage;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.LinkedList;
import java.util.concurrent.atomic.AtomicInteger;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;

public class RubyDebugTest {

private Debugger debugger;
private DebuggerSession debuggerSession;
private final LinkedList<Runnable> run = new LinkedList<>();
private SuspendedEvent suspendedEvent;
private Throwable ex;
private PolyglotEngine engine;
private final ByteArrayOutputStream out = new ByteArrayOutputStream();
private final ByteArrayOutputStream err = new ByteArrayOutputStream();

private static Source getSource(String path) {
InputStream stream = ClassLoader.getSystemResourceAsStream(path);
Reader reader = new InputStreamReader(stream);
try {
return Source.newBuilder(reader).name(new File(path).getName()).mimeType(RubyLanguage.MIME_TYPE).build();
} catch (IOException e) {
throw new Error(e);
}
}

@Before
public void before() throws IOException {
suspendedEvent = null;

engine = PolyglotEngine.newBuilder().setOut(out).setErr(err).build();
debugger = Debugger.find(engine);
debuggerSession = debugger.startSession(event -> {
suspendedEvent = event;
performWork();
suspendedEvent = null;
});

engine.eval(getSource("src/test/ruby/init.rb"));

run.clear();
}

@After
public void dispose() {
debuggerSession.close();
if (engine != null) {
engine.dispose();
}
}

@Test
public void testBreakpoint() throws Throwable {
final Source factorial = createFactorial();

run.addLast(() -> {
assertNull(suspendedEvent);
assertNotNull(debuggerSession);
Breakpoint breakpoint = Breakpoint.newBuilder(factorial).lineIs(3).build();
debuggerSession.install(breakpoint);
});

// Init before eval:
performWork();
engine.eval(factorial);

assertExecutedOK("Algorithm loaded");

assertLocation(3, "1",
"n", "1",
"nMinusOne", "nil",
"nMOFact", "nil",
"res", "nil");

continueExecution();

final Value main = engine.findGlobalSymbol("main");
assertNotNull( "main method found", main);
Value value = main.execute();
Number n = value.as(Number.class);
assertNotNull(n);
assertEquals("Factorial computed OK", 2, n.intValue());
assertExecutedOK("Algorithm computed OK: " + n + "; Checking if it stopped at the breakpoint");
}

@Test
public void stepInStepOver() throws Throwable {
final Source factorial = createFactorial();
engine.eval(factorial);
run.addLast(() -> {
assertNull(suspendedEvent);
assertNotNull(debuggerSession);
debuggerSession.suspendNextExecution();
});

assertLocation(13, "res = fac(2)", "res", "nil");
stepInto(1);
assertLocation(2, "if n <= 1",
"n", "2",
"nMinusOne", "nil",
"nMOFact", "nil",
"res", "nil");
stepOver(1);
assertLocation(5, "nMinusOne = n - 1",
"n", "2",
"nMinusOne", "nil",
"nMOFact", "nil",
"res", "nil");
stepOver(1);
assertLocation(6, "nMOFact = fac(nMinusOne)",
"n", "2",
"nMinusOne", "1",
"nMOFact", "nil",
"res", "nil");
stepOver(1);
assertLocation(7, "res = n * nMOFact",
"n", "2", "nMinusOne", "1",
"nMOFact", "1",
"res", "nil");
stepOut();
assertLocation(13, "res = fac(2)" + System.lineSeparator()
+ " puts res" + System.lineSeparator() // wrong!?
+ " res", // wrong!?
"res", "2");

continueExecution();

// Init before eval:
performWork();
Value value = engine.findGlobalSymbol("main").execute();

Number n = value.as(Number.class);

assertNotNull(n);
assertEquals("Factorial computed OK", 2, n.intValue());
assertExecutedOK("Stepping went OK");
}

private void performWork() {
try {
if (ex == null && !run.isEmpty()) {
Runnable c = run.removeFirst();
c.run();
}
} catch (Throwable e) {
ex = e;
}
}

private void stepOver(final int size) {
run.addLast(() -> suspendedEvent.prepareStepOver(size));
}

private void stepOut() {
run.addLast(() -> suspendedEvent.prepareStepOut());
}

private void continueExecution() {
run.addLast(() -> suspendedEvent.prepareContinue());
}

private void stepInto(final int size) {
run.addLast(() -> suspendedEvent.prepareStepInto(size));
}

private void assertLocation(final int line, final String code, final Object... expectedFrame) {
run.addLast(() -> {
assertNotNull(suspendedEvent);
final int currentLine = suspendedEvent.getSourceSection().getStartLine();
assertEquals(line, currentLine);
final String currentCode = suspendedEvent.getSourceSection().getCode().trim();
assertEquals(code, currentCode);
final DebugStackFrame frame = suspendedEvent.getTopStackFrame();

final AtomicInteger numFrameVars = new AtomicInteger(0);
frame.forEach(var -> { numFrameVars.incrementAndGet(); });
// There is (self) among the variables, hence substract 1:
assertEquals(expectedFrame.length / 2, numFrameVars.get() - 1);

for (int i = 0; i < expectedFrame.length; i = i + 2) {
String expectedIdentifier = (String) expectedFrame[i];
Object expectedValue = expectedFrame[i + 1];
DebugValue value = frame.getValue(expectedIdentifier);
assertNotNull(value);
String valueStr = value.as(String.class);

assertEquals(expectedValue, valueStr);
}

run.removeFirst().run();
});
}

private void assertExecutedOK(String msg) throws Throwable {
assertTrue(getErr(), getErr().isEmpty());

if (ex != null) {
if (ex instanceof AssertionError) {
throw ex;
} else {
throw new AssertionError(msg + ". Error during execution ", ex);
}
}

assertTrue(msg + ". Assuming all requests processed: " + run, run.isEmpty());
}

private static Source createFactorial() throws IOException {
return getSource("src/test/ruby/factorial.rb");
}

private final String getErr() {
try {
err.flush();
} catch (IOException e) {
}
return new String(err.toByteArray());
}

}