blob: e5d31251d76acbc9efc6c2b767c9fb37c7e43684 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2011, 2015 Ericsson and others.
* 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:
* Ericsson AB - Initial implementation of Test cases
*******************************************************************************/
package org.eclipse.cdt.tests.dsf.gdb.tests;
import java.util.concurrent.TimeUnit;
import org.eclipse.cdt.debug.core.ICDTLaunchConfigurationConstants;
import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor;
import org.eclipse.cdt.dsf.concurrent.ImmediateRequestMonitor;
import org.eclipse.cdt.dsf.concurrent.Query;
import org.eclipse.cdt.dsf.datamodel.DMContexts;
import org.eclipse.cdt.dsf.debug.service.IProcesses.IProcessDMContext;
import org.eclipse.cdt.dsf.debug.service.IRunControl.IExitedDMEvent;
import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService.ICommandControlShutdownDMEvent;
import org.eclipse.cdt.dsf.gdb.IGdbDebugPreferenceConstants;
import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin;
import org.eclipse.cdt.dsf.gdb.service.IGDBProcesses;
import org.eclipse.cdt.dsf.gdb.service.command.IGDBControl;
import org.eclipse.cdt.dsf.mi.service.IMIContainerDMContext;
import org.eclipse.cdt.dsf.mi.service.command.events.MIStoppedEvent;
import org.eclipse.cdt.dsf.service.DsfServicesTracker;
import org.eclipse.cdt.dsf.service.DsfSession;
import org.eclipse.cdt.tests.dsf.gdb.framework.BaseParametrizedTestCase;
import org.eclipse.cdt.tests.dsf.gdb.framework.ServiceEventWaitor;
import org.eclipse.cdt.tests.dsf.gdb.framework.SyncUtil;
import org.eclipse.cdt.tests.dsf.gdb.launching.TestsPlugin;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.preferences.IEclipsePreferences;
import org.eclipse.core.runtime.preferences.InstanceScope;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.osgi.service.prefs.Preferences;
/**
* Tests that we can perform different operations while the target
* is running.
*/
@RunWith(Parameterized.class)
public class OperationsWhileTargetIsRunningTest extends BaseParametrizedTestCase {
private DsfServicesTracker fServicesTracker;
private IGDBProcesses fProcesses;
private IMIContainerDMContext fContainerDmc;
private IGDBControl fControl;
private static final String EXEC_NAME = "TargetAvail.exe";
private static boolean fgAutoTerminate;
@BeforeClass
public static void doBeforeClass() throws Exception {
// Save the original values of the preferences used in this class
fgAutoTerminate = Platform.getPreferencesService().getBoolean(
GdbPlugin.PLUGIN_ID,
IGdbDebugPreferenceConstants.PREF_AUTO_TERMINATE_GDB,
true,
null );
}
@Override
public void doBeforeTest() throws Exception {
super.doBeforeTest();
final DsfSession session = getGDBLaunch().getSession();
Runnable runnable = new Runnable() {
@Override
public void run() {
fServicesTracker =
new DsfServicesTracker(TestsPlugin.getBundleContext(),
session.getId());
fProcesses = fServicesTracker.getService(IGDBProcesses.class);
fControl = fServicesTracker.getService(IGDBControl.class);
}
};
session.getExecutor().submit(runnable).get();
fContainerDmc = (IMIContainerDMContext)SyncUtil.getContainerContext();
}
@Override
public void doAfterTest() throws Exception {
super.doAfterTest();
if (fServicesTracker!=null) fServicesTracker.dispose();
// Restore the different preferences we might have changed
IEclipsePreferences node = InstanceScope.INSTANCE.getNode( GdbPlugin.PLUGIN_ID );
node.putBoolean( IGdbDebugPreferenceConstants.PREF_AUTO_TERMINATE_GDB, fgAutoTerminate );
}
@Override
protected void setLaunchAttributes() {
super.setLaunchAttributes();
setLaunchAttribute(ICDTLaunchConfigurationConstants.ATTR_PROGRAM_NAME,
EXEC_PATH + EXEC_NAME);
}
/**
* Test that the restart operation works properly while the target is running, and
* with the option to kill GDB after the process terminates, enabled.
*/
@Test
public void restartWhileTargetRunningKillGDB() throws Throwable {
// Restart is not supported for a remote session
if (isRemoteSession()) {
Assert.assertFalse("Restart operation should not be allowed for a remote session",
SyncUtil.canRestart());
return;
}
// First set the preference to kill GDB (although it should not happen in this test)
Preferences node = InstanceScope.INSTANCE.getNode(GdbPlugin.PLUGIN_ID);
node.putBoolean(IGdbDebugPreferenceConstants.PREF_AUTO_TERMINATE_GDB, true);
// The target is currently stopped. We resume to get it running
// then we do the restart, and confirm we are then stopped on main
SyncUtil.resume();
MIStoppedEvent stoppedEvent = SyncUtil.restart(getGDBLaunch());
String func = stoppedEvent.getFrame().getFunction();
Assert.assertTrue("Expected to be stopped at main, but is stopped at " + func,
"main".equals(func));
// Now make sure GDB is still alive
Assert.assertTrue("GDB should have been still alive", fControl.isActive());
}
/**
* Test that the restart operation works properly while the target is running, and
* with the option to kill GDB after the process terminates, disabled.
*/
@Test
public void restartWhileTargetRunningGDBAlive() throws Throwable {
// Restart is not supported for a remote session
if (isRemoteSession()) {
Assert.assertFalse("Restart operation should not be allowed for a remote session",
SyncUtil.canRestart());
return;
}
// First set the preference not to kill gdb
Preferences node = InstanceScope.INSTANCE.getNode(GdbPlugin.PLUGIN_ID);
node.putBoolean(IGdbDebugPreferenceConstants.PREF_AUTO_TERMINATE_GDB, false);
// The target is currently stopped. We resume to get it running
// then we do the restart, and confirm we are then stopped on main
SyncUtil.resume();
MIStoppedEvent stoppedEvent = SyncUtil.restart(getGDBLaunch());
String func = stoppedEvent.getFrame().getFunction();
Assert.assertTrue("Expected to be stopped at main, but is stopped at " + func,
"main".equals(func));
// Now make sure GDB is still alive
Assert.assertTrue("GDB should have been still alive", fControl.isActive());
}
/**
* Test that the terminate operation works properly while the target is running, and
* with the option to kill GDB after the process terminates, enabled.
*/
@Test
public void terminateWhileTargetRunningKillGDB() throws Throwable {
// First set the preference to kill GDB
Preferences node = InstanceScope.INSTANCE.getNode(GdbPlugin.PLUGIN_ID);
node.putBoolean(IGdbDebugPreferenceConstants.PREF_AUTO_TERMINATE_GDB, true);
// The target is currently stopped. We resume to get it running
// then we terminate, and confirm that we shutdown right away
SyncUtil.resume();
ServiceEventWaitor<ICommandControlShutdownDMEvent> shutdownEventWaitor = new ServiceEventWaitor<ICommandControlShutdownDMEvent>(
getGDBLaunch().getSession(),
ICommandControlShutdownDMEvent.class);
// Don't use a query here. The terminate, because it kills GDB, may not return right away
// but that is ok because we wait for a shutdown event right after
Runnable runnable = new Runnable() {
@Override
public void run() {
IProcessDMContext processDmc = DMContexts.getAncestorOfType(fContainerDmc, IProcessDMContext.class);
fProcesses.terminate(processDmc, new ImmediateRequestMonitor());
}
};
fProcesses.getExecutor().execute(runnable);
// The shutdown must happen quickly, which will confirm that it was
// our own terminate that did it. If it take longer, it indicates
// that the program terminated on its own, which is not what we want.
// See Bug 518643 for details as to length of this delay
shutdownEventWaitor.waitForEvent(TestsPlugin.massageTimeout(1000));
// Now make sure GDB is dead
Assert.assertTrue("GDB should have been terminated", !fControl.isActive());
}
/**
* Test that the terminate operation works properly while the target is running, and
* with the option to kill GDB after the process terminates, disabled.
*/
@Test
public void terminateWhileTargetRunningKeepGDBAlive() throws Throwable {
// First set the preference not to kill gdb
Preferences node = InstanceScope.INSTANCE.getNode(GdbPlugin.PLUGIN_ID);
node.putBoolean(IGdbDebugPreferenceConstants.PREF_AUTO_TERMINATE_GDB, false);
// The target is currently stopped. We resume to get it running
// then we terminate the process, and confirm that there are no more processes
SyncUtil.resume();
ServiceEventWaitor<IExitedDMEvent> exitedEventWaitor = new ServiceEventWaitor<IExitedDMEvent>(
getGDBLaunch().getSession(),
IExitedDMEvent.class);
Query<Object> query = new Query<Object>() {
@Override
protected void execute(final DataRequestMonitor<Object> rm) {
IProcessDMContext processDmc = DMContexts.getAncestorOfType(fContainerDmc, IProcessDMContext.class);
fProcesses.terminate(processDmc, rm);
}
};
{
fProcesses.getExecutor().execute(query);
query.get(TestsPlugin.massageTimeout(1000), TimeUnit.MILLISECONDS);
}
IExitedDMEvent event = exitedEventWaitor.waitForEvent(TestsPlugin.massageTimeout(500));
if (!(event.getDMContext() instanceof IMIContainerDMContext)) {
// This was the thread exited event, we want the container exited event
event = exitedEventWaitor.waitForEvent(TestsPlugin.massageTimeout(500));
}
// Make sure this event shows that the process was terminated
Assert.assertTrue("Process was not terminated", event.getDMContext() instanceof IMIContainerDMContext);
IMIContainerDMContext dmc = (IMIContainerDMContext)event.getDMContext();
Assert.assertTrue("Expected process " + fContainerDmc.getGroupId() + " but got " + dmc.getGroupId(),
fContainerDmc.getGroupId().equals(dmc.getGroupId()));
// Now make sure GDB is still alive
Assert.assertTrue("GDB should have been still alive", fControl.isActive());
}
/**
* Test that the detach operation works properly while the target is running, and
* with the option to kill GDB after the process terminates, enabled.
*/
@Test
public void detachWhileTargetRunningKillGDB() throws Throwable {
// First set the preference to kill GDB
Preferences node = InstanceScope.INSTANCE.getNode(GdbPlugin.PLUGIN_ID);
node.putBoolean(IGdbDebugPreferenceConstants.PREF_AUTO_TERMINATE_GDB, true);
// The target is currently stopped. We resume to get it running
// then we detach the process, and confirm that we are shutdown
SyncUtil.resume();
ServiceEventWaitor<ICommandControlShutdownDMEvent> shutdownEventWaitor = new ServiceEventWaitor<ICommandControlShutdownDMEvent>(
getGDBLaunch().getSession(),
ICommandControlShutdownDMEvent.class);
// Don't use a query here. Because GDB will be killed, the call to detach may not return right away
// but that is ok because we wait for a shutdown event right after
Runnable runnable = new Runnable() {
@Override
public void run() {
fProcesses.detachDebuggerFromProcess(fContainerDmc, new ImmediateRequestMonitor());
}
};
fProcesses.getExecutor().execute(runnable);
// The shutdown must happen quickly, which will confirm that it was
// our own terminate that did it. If it take longer, it indicates
// that the program terminated on its own, which is not what we want.
// See Bug 518643 for details as to length of this delay
shutdownEventWaitor.waitForEvent(TestsPlugin.massageTimeout(1000));
// Now make sure GDB is dead
Assert.assertTrue("GDB should have been terminated", !fControl.isActive());
}
/**
* Test that the detach operation works properly while the target is running, and
* with the option to kill GDB after the process terminates, disabled.
*/
@Test
public void detachWhileTargetRunningGDBAlive() throws Throwable {
// First set the preference not to kill gdb
Preferences node = InstanceScope.INSTANCE.getNode(GdbPlugin.PLUGIN_ID);
node.putBoolean(IGdbDebugPreferenceConstants.PREF_AUTO_TERMINATE_GDB, false);
// The target is currently stopped. We resume to get it running
// then we detach the process, and confirm that we are not longer running
SyncUtil.resume();
ServiceEventWaitor<IExitedDMEvent> exitedEventWaitor = new ServiceEventWaitor<IExitedDMEvent>(
getGDBLaunch().getSession(),
IExitedDMEvent.class);
Query<Object> query = new Query<Object>() {
@Override
protected void execute(final DataRequestMonitor<Object> rm) {
fProcesses.detachDebuggerFromProcess(fContainerDmc, rm);
}
};
{
fProcesses.getExecutor().execute(query);
query.get(TestsPlugin.massageTimeout(1000), TimeUnit.MILLISECONDS);
}
IExitedDMEvent event = exitedEventWaitor.waitForEvent(TestsPlugin.massageTimeout(500));
if (!(event.getDMContext() instanceof IMIContainerDMContext)) {
// This was the thread exited event, we want the container exited event
event = exitedEventWaitor.waitForEvent(TestsPlugin.massageTimeout(500));
}
// Make sure this event shows that the process was detached
Assert.assertTrue("Process was not detached", event.getDMContext() instanceof IMIContainerDMContext);
IMIContainerDMContext dmc = (IMIContainerDMContext)event.getDMContext();
Assert.assertTrue("Expected process " + fContainerDmc.getGroupId() + " but got " + dmc.getGroupId(),
fContainerDmc.getGroupId().equals(dmc.getGroupId()));
// Now make sure GDB is still alive
Assert.assertTrue("GDB should have been still alive", fControl.isActive());
}
}