/******************************************************************************* | |
* Copyright (c) 2010 Nokia 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: | |
* Nokia - Initial API and implementation | |
*******************************************************************************/ | |
package org.eclipse.cdt.debug.edc.debugger.tests; | |
import java.util.ArrayList; | |
import java.util.HashMap; | |
import java.util.Map; | |
import java.util.concurrent.ExecutionException; | |
import java.util.concurrent.TimeUnit; | |
import java.util.concurrent.TimeoutException; | |
import org.eclipse.cdt.debug.core.CDebugCorePlugin; | |
import org.eclipse.cdt.debug.core.breakpointactions.BreakpointActionManager; | |
import org.eclipse.cdt.debug.core.breakpointactions.IBreakpointAction; | |
import org.eclipse.cdt.debug.edc.internal.EDCDebugger; | |
import org.eclipse.cdt.debug.edc.internal.breakpointactions.SkipAction; | |
import org.eclipse.cdt.debug.edc.internal.services.dsf.Breakpoints; | |
import org.eclipse.cdt.debug.edc.internal.services.dsf.RunControl; | |
import org.eclipse.cdt.debug.edc.services.EDCServicesTracker; | |
import org.eclipse.cdt.debug.edc.services.Stack; | |
import org.eclipse.cdt.debug.edc.tests.TestUtils; | |
import org.eclipse.cdt.debug.ui.breakpointactions.LogAction; | |
import org.eclipse.cdt.debug.ui.breakpointactions.ResumeAction; | |
import org.eclipse.cdt.debug.ui.breakpointactions.SoundAction; | |
import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; | |
import org.eclipse.cdt.dsf.concurrent.Query; | |
import org.eclipse.cdt.dsf.debug.service.IBreakpoints; | |
import org.eclipse.cdt.dsf.debug.service.IRunControl2; | |
import org.eclipse.cdt.dsf.debug.service.IStack; | |
import org.eclipse.core.runtime.IStatus; | |
import org.junit.AfterClass; | |
import org.junit.Assert; | |
import org.junit.BeforeClass; | |
import org.junit.Test; | |
/** | |
TODO this really only tests run-control scenarios for breakpoint | |
actions. SkipAction & ResumeAction can be considered fully tested. | |
however, LogAction & SoundAction are only tested in terms of | |
coverage and not crashing. nothing checks the log to see if the | |
log was appropriately written, or if an expression was appropriatly | |
evaluated, and there's no automatic check for the Sound being played. | |
*/ | |
public class BreakpointActionsTest { | |
private static final String BlackFlagWascana_cpp = "BlackFlagWascana.cpp"; //$NON-NLS-1$ | |
private static final String dbg_simple_types_cpp = "dbg_simple_types.cpp"; //$NON-NLS-1$ | |
private static final String BlackFlagWascana_cpp_path | |
= "C:\\myprog\\BlackFlagWascana\\src\\BlackFlagWascana.cpp"; //$NON-NLS-1$ | |
private static final String dbg_breakpoints_cpp_path | |
= "C:\\myprog\\BlackFlagWascana\\src\\dbg_breakpoints.cpp"; //$NON-NLS-1$ | |
private static final String dbg_derived_types_cpp_path | |
= "C:\\myprog\\BlackFlagWascana\\src\\dbg_derived_types.cpp"; //$NON-NLS-1$ | |
private static final String dbg_memory_cpp_path | |
= "C:\\myprog\\BlackFlagWascana\\src\\dbg_memory.cpp"; //$NON-NLS-1$ | |
private static final String dbg_rtti_cpp_path | |
= "C:\\myprog\\BlackFlagWascana\\src\\dbg_rtti.cpp"; //$NON-NLS-1$ | |
private static final String dbg_simple_types_cpp_path | |
= "C:\\myprog\\BlackFlagWascana\\src\\dbg_simple_types.cpp"; //$NON-NLS-1$ | |
/** | |
* these mementos are subject to change | |
*/ | |
private static final String bpActionMemento | |
= "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>"; | |
private static final String log8Data | |
= bpActionMemento + "<logData evalExpr=\"true\" message=\"3+5\"/>"; | |
private static final String logHereData | |
= bpActionMemento + "<logData evalExpr=\"false\" message=\"here\"/>"; | |
private static final String pause6Data | |
= bpActionMemento + "<resumeData pauseTime=\"6\"/>"; | |
private static final String skip2Data | |
= bpActionMemento + "<skipData statements=\"2\"/>"; | |
private static final String soundData | |
= bpActionMemento + "<soundData file=\"C:\\WINDOWS\\Media\\tada.wav\"/>"; | |
static enum BPAction { | |
Log8(LogAction.class, "log8", log8Data), | |
LogHere(LogAction.class, "logHere", logHereData), | |
pause6(ResumeAction.class, "pause6", pause6Data), | |
Skip2(SkipAction.class, "skip2", skip2Data), | |
SoundTada(SoundAction.class, "soundTada", soundData); | |
final Class<? extends IBreakpointAction> c; | |
final String name; | |
final String memento; | |
BPAction(Class<? extends IBreakpointAction> c, String name, String memento) { | |
this.c = c; | |
this.name = name; | |
this.memento = memento; | |
} | |
Class<? extends IBreakpointAction> getActionClass() { | |
return c; | |
} | |
String getName() { | |
return name; | |
} | |
String getMemento() { | |
return memento; | |
} | |
} | |
static enum BPData { | |
main16(BlackFlagWascana_cpp_path, 16, BPAction.Skip2.getName()), | |
main18(BlackFlagWascana_cpp_path, 18, BPAction.Log8.getName()), | |
main19(BlackFlagWascana_cpp_path, 19, BPAction.Skip2.getName()), | |
main23(BlackFlagWascana_cpp_path, 23, BPAction.pause6.getName()), | |
main24(BlackFlagWascana_cpp_path, 24, BPAction.LogHere.getName()), | |
main25(BlackFlagWascana_cpp_path, 25, BPAction.LogHere.getName()+","+BPAction.SoundTada.getName()), | |
dbg_breakpoints(dbg_breakpoints_cpp_path, 56, null), | |
dbg_dervied_types(dbg_derived_types_cpp_path, 285, null), | |
dbg_memory(dbg_memory_cpp_path, 9, null), | |
dbg_rtti(dbg_rtti_cpp_path, 47, null), | |
dbg_simple_types(dbg_simple_types_cpp_path, 60, null) | |
; | |
final String file; | |
final int line; | |
final String actionPropString; | |
BPData(String file, int line, String actionPropString) { | |
this.file = file; | |
this.line = line; | |
this.actionPropString = actionPropString; | |
} | |
String getFile() { | |
return file; | |
} | |
int getLine() { | |
return line; | |
} | |
boolean hasActions() { | |
return actionPropString != null; | |
} | |
String getActions() { | |
return actionPropString; | |
} | |
} | |
static BreakpointActionManager bpActionMgr() { | |
return CDebugCorePlugin.getDefault().getBreakpointActionManager(); | |
} | |
private static Breakpoints breakpointsService; | |
private static ArrayList<IBreakpoints.IBreakpointDMContext> bkpts; | |
private static BaseLaunchTest simpleLaunch; | |
private static IRunControl2 runControl; | |
private static IStack stackService; | |
@BeforeClass | |
public static void setUpClass() throws Exception { | |
simpleLaunch = new BaseLaunchTest() { | |
@Override | |
protected String getExeFileName() { | |
// This is the executable built by Cygwin gcc 3.4.4 | |
// All source files are built from this folder: | |
// "C:\\myprog\\BlackFlagWascana\\src\\" | |
// Note we don't need any source file to perform the test. | |
// | |
// used because the locations of certain line numbers are known, | |
// not because there's no hardcoded break | |
return "BlackFlagMinGW_NoHardcodedBreak.exe"; //$NON-NLS-1$ | |
} | |
@Override | |
protected IStack getStackService() { | |
return stackService; | |
} | |
@Override | |
protected boolean getStopAtMain() { | |
return true; | |
} | |
}; | |
simpleLaunch.basicLaunch(); | |
EDCServicesTracker edcTracker | |
= new EDCServicesTracker( | |
EDCDebugger.getBundleContext(), simpleLaunch.session.getId()); | |
Assert.assertNotNull(edcTracker); | |
breakpointsService = edcTracker.getService(Breakpoints.class); | |
Assert.assertNotNull(breakpointsService); | |
runControl = edcTracker.getService(RunControl.class); | |
Assert.assertNotNull(runControl); | |
stackService = edcTracker.getService(Stack.class); | |
Assert.assertNotNull(stackService); | |
for (BPAction actionData : BPAction.values()) { | |
IBreakpointAction action | |
= actionData.getActionClass().getConstructor((Class[])null).newInstance((Object[])null); | |
action.setName(actionData.getName()); | |
action.initializeFromMemento(actionData.getMemento()); | |
bpActionMgr().addAction(action); | |
} | |
BPData[] allData = BPData.values(); | |
bkpts = new ArrayList<IBreakpoints.IBreakpointDMContext>(allData.length); | |
TestUtils.waitForUIUpdate(1000); | |
} | |
private static IBreakpoints.IBreakpointDMContext setBreakpointWithAction(BPData bpData) | |
throws Exception, InterruptedException, ExecutionException, | |
TimeoutException { | |
String file = bpData.getFile(); | |
int line = bpData.getLine(); | |
final IBreakpoints.IBreakpointDMContext bp | |
= simpleLaunch.setUserBreakpoint(breakpointsService, file, line); | |
Assert.assertNotNull(bkpts); | |
if (bpData.hasActions()) { | |
final Map<String, Object> delta = new HashMap<String,Object>(); | |
delta.put(BreakpointActionManager.BREAKPOINT_ACTION_ATTRIBUTE, bpData.getActions()); | |
Query<IStatus> query = new Query<IStatus>() { | |
@Override | |
protected void execute(final DataRequestMonitor<IStatus> drm) { | |
breakpointsService.updateBreakpoint(bp, delta, drm); | |
} | |
}; | |
simpleLaunch.session.getExecutor().execute(query); | |
query.get(1, TimeUnit.SECONDS); | |
} | |
return bp; | |
} | |
@AfterClass | |
public static void shutdown() { | |
for (final IBreakpoints.IBreakpointDMContext bpc : bkpts) { | |
try { | |
simpleLaunch.removeUserBreakpoint(breakpointsService, bpc); | |
} catch (Exception e) { | |
e.printStackTrace(); | |
} | |
} | |
for (BPAction bpAction : BPAction.values()) { | |
IBreakpointAction action | |
= bpActionMgr().findBreakpointAction(bpAction.getName()); | |
bpActionMgr().deleteAction(action); | |
} | |
TestUtils.shutdownDebugSession(simpleLaunch.launch, simpleLaunch.session); | |
bkpts = null; | |
breakpointsService = null; | |
simpleLaunch = null; | |
} | |
// for all tests, the target in waitRunToLine() has been semi-arbitrarily | |
// chosen as 26, because it's after the last test breakpoint | |
private void runToNextExpectedBreakpoint(int waitTime) throws Exception { | |
simpleLaunch.waitRunToLine(runControl, BlackFlagWascana_cpp_path, 26, 500); | |
TestUtils.waitForUIUpdate(waitTime); | |
} | |
// default 1/2 second wait | |
private void runToNextExpectedBreakpoint() throws Exception { | |
runToNextExpectedBreakpoint(500); | |
} | |
@Test | |
public void testBreakpointActionSkipActionEndingAtBreakpoint() throws Exception { | |
// bkpt16 has a skip2 skip action attached | |
// and thus skips over nested bkpts in dbg_breakpoints() | |
// & dbg_derived_types(). should end at bkpt at line 18 in main | |
// bkpt18 has a log action attached, which should execute | |
bkpts.add(setBreakpointWithAction(BPData.main16)); | |
bkpts.add(setBreakpointWithAction(BPData.main18)); | |
bkpts.add(setBreakpointWithAction(BPData.dbg_breakpoints)); | |
bkpts.add(setBreakpointWithAction(BPData.dbg_dervied_types)); | |
runToNextExpectedBreakpoint(); | |
simpleLaunch.assertControlIsAt(BlackFlagWascana_cpp, "main", BPData.main18.getLine()); | |
} | |
@Test | |
public void testBreakpointActionsSkipActionNoEndingAtBreakpoint() throws Exception { | |
// bkpt19 has a skip2 skip action attached, | |
// skips over nested breakpoints in dbg_memory() & dbg_rtti() | |
// should end in nested bkpt in dbg_simple_types() | |
bkpts.add(setBreakpointWithAction(BPData.main19)); | |
bkpts.add(setBreakpointWithAction(BPData.dbg_memory)); | |
bkpts.add(setBreakpointWithAction(BPData.dbg_rtti)); | |
bkpts.add(setBreakpointWithAction(BPData.dbg_simple_types)); | |
// was encountering occasional timing based errors when running this test. | |
// wait a little longer because the stack changes for this test where it | |
// is not changing for the other tests in this JUnit | |
runToNextExpectedBreakpoint(/* waitTime */ 1000); | |
simpleLaunch.assertControlIsAt(dbg_simple_types_cpp, "dbg_simple_types", BPData.dbg_simple_types.getLine()); | |
} | |
@Test | |
public void testBreakpointActionsResumeActionPause6() throws Exception { | |
// bkpt23 has a pause6 resume action attached. | |
// so a check after 3 seconds should show we're still here | |
// and a check 6 seconds after that should show we're at the next bkpt | |
// should end at bkpt24 | |
bkpts.add(setBreakpointWithAction(BPData.main23)); | |
bkpts.add(setBreakpointWithAction(BPData.main24)); | |
runToNextExpectedBreakpoint(); | |
// we should be at our breakpoint right after it's hit | |
simpleLaunch.assertControlIsAt(BlackFlagWascana_cpp, "main", BPData.main23.getLine()); | |
simpleLaunch.updateSuspendedFrame(3000); // half the length of the pause | |
// should still be at the same line (i.e. still pausing) | |
simpleLaunch.assertControlIsAt(BlackFlagWascana_cpp, "main", BPData.main23.getLine()); | |
simpleLaunch.updateSuspendedFrame(4000); // the other half of the pause plus a little cushion | |
// should be done with the pause, and resumed to the next line | |
simpleLaunch.assertControlIsAt(BlackFlagWascana_cpp, "main", BPData.main24.getLine()); | |
} | |
@Test | |
public void testBreakpointActionsSoundAction() throws Exception { | |
// bkpt25 has two actions attached | |
// ... uh, unsure how to really test if both were processed | |
bkpts.add(setBreakpointWithAction(BPData.main25)); | |
runToNextExpectedBreakpoint(); | |
Thread.sleep(100); // wait for sound to be played | |
simpleLaunch.assertControlIsAt(BlackFlagWascana_cpp, "main", BPData.main25.getLine()); | |
} | |
} |