/*******************************************************************************
* Copyright (c) 2012-2017 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.plugin.gdb.server;
import com.google.common.collect.ImmutableMap;
import org.eclipse.che.api.debugger.server.Debugger;
import org.eclipse.che.api.debugger.server.exceptions.DebuggerException;
import org.eclipse.che.api.debug.shared.model.Breakpoint;
import org.eclipse.che.api.debug.shared.model.DebuggerInfo;
import org.eclipse.che.api.debug.shared.model.Location;
import org.eclipse.che.api.debug.shared.model.StackFrameDump;
import org.eclipse.che.api.debug.shared.model.SimpleValue;
import org.eclipse.che.api.debug.shared.model.Variable;
import org.eclipse.che.api.debug.shared.model.VariablePath;
import org.eclipse.che.api.debug.shared.model.event.BreakpointActivatedEvent;
import org.eclipse.che.api.debug.shared.model.event.DebuggerEvent;
import org.eclipse.che.api.debug.shared.model.event.DisconnectEvent;
import org.eclipse.che.api.debug.shared.model.event.SuspendEvent;
import org.eclipse.che.api.debug.shared.model.impl.BreakpointImpl;
import org.eclipse.che.api.debug.shared.model.impl.LocationImpl;
import org.eclipse.che.api.debug.shared.model.impl.VariableImpl;
import org.eclipse.che.api.debug.shared.model.impl.VariablePathImpl;
import org.eclipse.che.api.debug.shared.model.impl.action.ResumeActionImpl;
import org.eclipse.che.api.debug.shared.model.impl.action.StartActionImpl;
import org.eclipse.che.api.debug.shared.model.impl.action.StepOutActionImpl;
import org.eclipse.che.api.debug.shared.model.impl.action.StepOverActionImpl;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertTrue;
/**
* @author Anatoliy Bazko
*/
public class GdbDebuggerTest {
private String file;
private Path sourceDirectory;
private GdbServer gdbServer;
private Debugger gdbDebugger;
private BlockingQueue<DebuggerEvent> events;
@BeforeClass
public void beforeClass() throws Exception {
file = GdbTest.class.getResource("/hello").getFile();
sourceDirectory = Paths.get(GdbTest.class.getResource("/h.cpp").getFile());
events = new ArrayBlockingQueue<>(10);
}
@BeforeMethod
public void setUp() throws Exception {
gdbServer = GdbServer.start("localhost", 1111, file);
}
@AfterMethod
public void tearDown() throws Exception {
gdbServer.stop();
}
@Test
public void testDebugger() throws Exception {
initializeDebugger();
addBreakpoint();
startDebugger();
doSetAndGetValues();
// stepInto();
stepOver();
stepOut();
resume();
deleteAllBreakpoints();
disconnect();
}
private void deleteAllBreakpoints() throws DebuggerException {
List<Breakpoint> breakpoints = gdbDebugger.getAllBreakpoints();
assertEquals(breakpoints.size(), 1);
gdbDebugger.deleteAllBreakpoints();
breakpoints = gdbDebugger.getAllBreakpoints();
assertTrue(breakpoints.isEmpty());
}
private void resume() throws DebuggerException, InterruptedException {
gdbDebugger.resume(new ResumeActionImpl());
DebuggerEvent debuggerEvent = events.take();
assertTrue(debuggerEvent instanceof SuspendEvent);
SuspendEvent suspendEvent = (SuspendEvent)debuggerEvent;
assertEquals(suspendEvent.getLocation().getTarget(), "h.cpp");
assertEquals(suspendEvent.getLocation().getLineNumber(), 7);
}
private void stepOut() throws DebuggerException, InterruptedException {
try {
gdbDebugger.stepOut(new StepOutActionImpl());
} catch (DebuggerException e) {
// ignore
}
DebuggerEvent debuggerEvent = events.take();
assertTrue(debuggerEvent instanceof SuspendEvent);
}
private void stepOver() throws DebuggerException, InterruptedException {
gdbDebugger.stepOver(new StepOverActionImpl());
DebuggerEvent debuggerEvent = events.take();
assertTrue(debuggerEvent instanceof SuspendEvent);
SuspendEvent suspendEvent = (SuspendEvent)debuggerEvent;
assertEquals(suspendEvent.getLocation().getTarget(), "h.cpp");
assertEquals(suspendEvent.getLocation().getLineNumber(), 5);
gdbDebugger.stepOver(new StepOverActionImpl());
debuggerEvent = events.take();
assertTrue(debuggerEvent instanceof SuspendEvent);
suspendEvent = (SuspendEvent)debuggerEvent;
assertEquals(suspendEvent.getLocation().getTarget(), "h.cpp");
assertEquals(suspendEvent.getLocation().getLineNumber(), 6);
gdbDebugger.stepOver(new StepOverActionImpl());
debuggerEvent = events.take();
assertTrue(debuggerEvent instanceof SuspendEvent);
suspendEvent = (SuspendEvent)debuggerEvent;
assertEquals(suspendEvent.getLocation().getTarget(), "h.cpp");
assertEquals(suspendEvent.getLocation().getLineNumber(), 7);
}
private void doSetAndGetValues() throws DebuggerException {
VariablePath variablePath = new VariablePathImpl("i");
Variable variable = new VariableImpl("int", "i", "2", true, variablePath, Collections.emptyList(), false);
SimpleValue value = gdbDebugger.getValue(variablePath);
assertEquals(value.getValue(), "0");
gdbDebugger.setValue(variable);
value = gdbDebugger.getValue(variablePath);
assertEquals(value.getValue(), "2");
String expression = gdbDebugger.evaluate("i");
assertEquals(expression, "2");
expression = gdbDebugger.evaluate("10 + 10");
assertEquals(expression, "20");
StackFrameDump stackFrameDump = gdbDebugger.dumpStackFrame();
assertTrue(stackFrameDump.getFields().isEmpty());
assertEquals(stackFrameDump.getVariables().size(), 1);
assertEquals(stackFrameDump.getVariables().get(0).getName(), "i");
assertEquals(stackFrameDump.getVariables().get(0).getValue(), "2");
assertEquals(stackFrameDump.getVariables().get(0).getType(), "int");
}
private void startDebugger() throws DebuggerException, InterruptedException {
gdbDebugger.start(new StartActionImpl(Collections.emptyList()));
assertEquals(events.size(), 1);
DebuggerEvent debuggerEvent = events.take();
assertTrue(debuggerEvent instanceof SuspendEvent);
SuspendEvent suspendEvent = (SuspendEvent)debuggerEvent;
assertEquals(suspendEvent.getLocation().getTarget(), "h.cpp");
assertEquals(suspendEvent.getLocation().getLineNumber(), 7);
}
private void disconnect() throws DebuggerException, InterruptedException {
gdbDebugger.disconnect();
assertEquals(events.size(), 1);
DebuggerEvent debuggerEvent = events.take();
assertTrue(debuggerEvent instanceof DisconnectEvent);
}
private void addBreakpoint() throws DebuggerException, InterruptedException {
Location location = new LocationImpl("h.cpp", 7);
Breakpoint breakpoint = new BreakpointImpl(location);
gdbDebugger.addBreakpoint(breakpoint);
assertEquals(events.size(), 1);
DebuggerEvent debuggerEvent = events.take();
assertTrue(debuggerEvent instanceof BreakpointActivatedEvent);
BreakpointActivatedEvent breakpointActivatedEvent = (BreakpointActivatedEvent)debuggerEvent;
assertEquals(breakpointActivatedEvent.getBreakpoint().getLocation().getTarget(), "h.cpp");
assertEquals(breakpointActivatedEvent.getBreakpoint().getLocation().getLineNumber(), 7);
}
private void initializeDebugger() throws DebuggerException {
Map<String, String> properties = ImmutableMap.of("host", "localhost",
"port", "1111",
"binary", file,
"sources", sourceDirectory.getParent().toString());
GdbDebuggerFactory gdbDebuggerFactory = new GdbDebuggerFactory();
gdbDebugger = gdbDebuggerFactory.create(properties, events::add);
DebuggerInfo debuggerInfo = gdbDebugger.getInfo();
assertEquals(debuggerInfo.getFile(), file);
assertEquals(debuggerInfo.getHost(), "localhost");
assertEquals(debuggerInfo.getPort(), 1111);
assertNotNull(debuggerInfo.getName());
assertNotNull(debuggerInfo.getVersion());
}
}