package org.python.pydev.debug.ui; import org.eclipse.debug.core.DebugPlugin; import org.eclipse.debug.core.ILaunch; import org.eclipse.debug.core.ILaunchManager; import org.eclipse.debug.core.model.IDebugTarget; import org.eclipse.debug.core.model.IThread; import org.eclipse.jface.text.source.IVerticalRulerInfo; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Display; import org.eclipse.ui.IWorkbench; import org.eclipse.ui.IWorkbenchWindow; import org.eclipse.ui.WorkbenchException; import org.python.pydev.debug.model.PyDebugTarget; import org.python.pydev.debug.ui.actions.PyBreakpointRulerAction; import org.python.pydev.debug.ui.launching.JythonLaunchShortcut; import org.python.pydev.editor.PyEdit; import org.python.pydev.plugin.PydevPlugin; import org.python.pydev.utils.ICallback; public class DebuggerTestUtils { private PyEdit debugEditor; /** * Maximum number of loops (used with the timeout) */ private static final int MAX_LOOPS = 300; /** * Maximum time for each loop in millis */ private static final int STEP_TIMEOUT = 100; /** * Number of steps in the tests that will have busy loops until some condition is hit. */ private static final int TOTAL_STEPS = 3; /** * Total time in millis that the test has for finishing */ public static final int TOTAL_TIME_FOR_TESTS = MAX_LOOPS * STEP_TIMEOUT * (TOTAL_STEPS + 1); /** * Used for having wait() */ private Object lock = new Object(); /** * An exception that occurred that was thrown and didn't let the tests finish */ public Throwable failException = null; public DebuggerTestUtils(PyEdit debugEditor2) { this.debugEditor = debugEditor2; } /** * @return an action that can be run to create a breakpoint in the given line */ public PyBreakpointRulerAction createAddBreakPointAction(final int line) { PyBreakpointRulerAction ret = new PyBreakpointRulerAction(debugEditor, new IVerticalRulerInfo() { @Override public int getLineOfLastMouseButtonActivity() { return line; } @Override public Control getControl() { throw new RuntimeException("Not Implemented"); } @Override public int getWidth() { throw new RuntimeException("Not Implemented"); } @Override public int toDocumentLineNumber(int y_coordinate) { throw new RuntimeException("Not Implemented"); } }); ret.update(); return ret; } /** * Creates a run in debug mode for the debug editor */ public void launchEditorInDebug() { final IWorkbench workBench = PydevPlugin.getDefault().getWorkbench(); Display display = workBench.getDisplay(); // Make sure to run the UI thread. display.syncExec(new Runnable() { @Override public void run() { JythonLaunchShortcut launchShortcut = new JythonLaunchShortcut(); launchShortcut.launch(debugEditor, "debug"); } }); } /** * Waits until some thread is suspended. */ protected IThread waitForSuspendedThread(final PyDebugTarget target) throws Throwable { final IThread[] ret = new IThread[1]; waitForCondition(new ICallback() { @Override public Object call(Object args) throws Exception { IThread[] threads = target.getThreads(); for (IThread thread : threads) { if (thread.isSuspended()) { ret[0] = thread; return true; } } return false; } }, "waitForSuspendedThread"); return ret[0]; } /** * Waits until a launch becomes available * @return the launch that was found */ public ILaunch waitForLaunchAvailable() throws Throwable { final ILaunchManager launchManager = DebugPlugin.getDefault().getLaunchManager(); waitForCondition(new ICallback() { @Override public Object call(Object args) throws Exception { ILaunch[] launches = launchManager.getLaunches(); return launches.length > 0; } }, "waitForLaunchAvailable"); return launchManager.getLaunches()[0]; } /** * Waits until a debug target is available in the passed launch * @return the debug target found */ public IDebugTarget waitForDebugTargetAvailable(final ILaunch launch) throws Throwable { waitForCondition(new ICallback() { @Override public Object call(Object args) throws Exception { return launch.getDebugTarget() != null; } }, "waitForDebugTargetAvailable"); return launch.getDebugTarget(); } /** * Keeps on a busy loop with a timeout until the given callback returns true (otherwise, an * exception is thrown when the total time is elapsed). */ public void waitForCondition(ICallback callback, String errorMessage) throws Throwable { if (failException != null) { throw failException; } int loops = MAX_LOOPS; for (int i = 0; i < loops; i++) { if ((Boolean) callback.call(new Object[] {})) { return; } synchronized (lock) { try { Thread.yield(); lock.wait(STEP_TIMEOUT); } catch (InterruptedException e) { } } } throw new AssertionError("Unable to get to condition after " + (loops * STEP_TIMEOUT) / 1000 + " seconds.\nMessage: " + errorMessage); } /** * This method can be used to switch to a given perspective * @param perspectiveId the id of the perspective that should be activated. * @return the exception raised or null. */ public void switchToPerspective(final String perspectiveId) { final IWorkbench workBench = PydevPlugin.getDefault().getWorkbench(); Display display = workBench.getDisplay(); // Make sure to run the UI thread. display.syncExec(new Runnable() { @Override public void run() { IWorkbenchWindow window = workBench.getActiveWorkbenchWindow(); try { workBench.showPerspective(perspectiveId, window); } catch (WorkbenchException e) { failException = e; } } }); } }