diff options
author | Pawel Piech | 2012-01-07 01:00:46 +0000 |
---|---|---|
committer | Eugene Tarassov | 2012-01-09 22:51:32 +0000 |
commit | 58086538560ea8e6ac5f9f9c64f9f9f1eed506f4 (patch) | |
tree | e26ebf34af61aa0247acb94ad390a03ce5268f04 | |
parent | 8628e7e335020bf7cc1f5bf5dd39c6a97843464f (diff) | |
download | org.eclipse.tcf-58086538560ea8e6ac5f9f9c64f9f9f1eed506f4.tar.gz org.eclipse.tcf-58086538560ea8e6ac5f9f9c64f9f9f1eed506f4.tar.xz org.eclipse.tcf-58086538560ea8e6ac5f9f9c64f9f9f1eed506f4.zip |
Test framework update.
34 files changed, 3595 insertions, 665 deletions
diff --git a/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFModelProxy.java b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFModelProxy.java index bfa8ca86d..99bfe77b5 100644 --- a/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFModelProxy.java +++ b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFModelProxy.java @@ -21,6 +21,7 @@ import java.util.Set; import org.eclipse.core.runtime.IStatus; import org.eclipse.debug.internal.ui.viewers.model.ITreeModelViewer; import org.eclipse.debug.internal.ui.viewers.model.InternalTreeModelViewer; +import org.eclipse.debug.internal.ui.viewers.model.InternalVirtualTreeModelViewer; import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenCountUpdate; import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenUpdate; import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta; @@ -426,7 +427,11 @@ public class TCFModelProxy extends AbstractModelProxy implements IModelProxy, Ru asyncExec(new Runnable() { boolean found; public void run() { - found = ((InternalTreeModelViewer)getViewer()).findElementIndex(TreePath.EMPTY, launch) >= 0; + if (getViewer() instanceof InternalTreeModelViewer) { + found = ((InternalTreeModelViewer)getViewer()).findElementIndex(TreePath.EMPTY, launch) >= 0; + } else if (getViewer() instanceof InternalVirtualTreeModelViewer) { + found = ((InternalVirtualTreeModelViewer)getViewer()).findElementIndex(TreePath.EMPTY, launch) >= 0; + } Protocol.invokeLater(new Runnable() { public void run() { if (disposed) return; diff --git a/tests/plugins/org.eclipse.tcf.debug.test/META-INF/MANIFEST.MF b/tests/plugins/org.eclipse.tcf.debug.test/META-INF/MANIFEST.MF index 48627ce45..bbb668758 100644 --- a/tests/plugins/org.eclipse.tcf.debug.test/META-INF/MANIFEST.MF +++ b/tests/plugins/org.eclipse.tcf.debug.test/META-INF/MANIFEST.MF @@ -10,7 +10,8 @@ Require-Bundle: org.eclipse.ui, org.eclipse.tcf.debug.ui, org.eclipse.tcf.core, org.junit;bundle-version="3.8.2", - org.eclipse.debug.ui;bundle-version="3.7.0" + org.eclipse.debug.ui;bundle-version="3.7.0", + org.eclipse.cdt.debug.core;bundle-version="7.2.0" Bundle-ActivationPolicy: lazy Bundle-RequiredExecutionEnvironment: J2SE-1.5 Import-Package: org.eclipse.debug.internal.ui.viewers.model diff --git a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/AbstractTcfUITest.java b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/AbstractTcfUITest.java index efd2294a2..587682467 100644 --- a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/AbstractTcfUITest.java +++ b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/AbstractTcfUITest.java @@ -9,10 +9,10 @@ import java.util.Map; import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.regex.Pattern; import junit.framework.TestCase; -import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.debug.core.DebugException; import org.eclipse.debug.core.DebugPlugin; @@ -23,25 +23,33 @@ import org.eclipse.debug.core.ILaunchManager; import org.eclipse.debug.core.ILaunchesListener2; import org.eclipse.debug.core.model.IDisconnect; import org.eclipse.debug.internal.ui.viewers.model.provisional.PresentationContext; +import org.eclipse.debug.internal.ui.viewers.model.provisional.VirtualItem; import org.eclipse.debug.internal.ui.viewers.model.provisional.VirtualTreeModelViewer; import org.eclipse.debug.ui.IDebugUIConstants; import org.eclipse.swt.SWT; import org.eclipse.swt.widgets.Display; import org.eclipse.tcf.core.TransientPeer; +import org.eclipse.tcf.debug.test.services.BreakpointsCM; +import org.eclipse.tcf.debug.test.services.DiagnosticsCM; +import org.eclipse.tcf.debug.test.services.RunControlCM; +import org.eclipse.tcf.debug.test.services.RunControlCM.ContextState; +import org.eclipse.tcf.debug.test.services.StackTraceCM; +import org.eclipse.tcf.debug.test.services.SymbolsCM; import org.eclipse.tcf.debug.test.util.AggregateCallback; import org.eclipse.tcf.debug.test.util.Callback; +import org.eclipse.tcf.debug.test.util.Callback.ICanceledListener; import org.eclipse.tcf.debug.test.util.DataCallback; +import org.eclipse.tcf.debug.test.util.ICache; import org.eclipse.tcf.debug.test.util.Query; import org.eclipse.tcf.debug.test.util.Task; -import org.eclipse.tcf.debug.test.util.Callback.ICanceledListener; +import org.eclipse.tcf.debug.test.util.Transaction; import org.eclipse.tcf.protocol.IChannel; import org.eclipse.tcf.protocol.IPeer; -import org.eclipse.tcf.protocol.IToken; import org.eclipse.tcf.protocol.Protocol; import org.eclipse.tcf.services.IBreakpoints; import org.eclipse.tcf.services.IDiagnostics; -import org.eclipse.tcf.services.IDiagnostics.ISymbol; import org.eclipse.tcf.services.IExpressions; +import org.eclipse.tcf.services.IMemoryMap; import org.eclipse.tcf.services.IRunControl; import org.eclipse.tcf.services.IRunControl.RunControlContext; import org.eclipse.tcf.services.IStackTrace; @@ -56,6 +64,7 @@ public abstract class AbstractTcfUITest extends TestCase implements IViewerUpdat private final static int NUM_CHANNELS = 1; + protected IChannel[] channels; private Query<Object> fMonitorChannelQuery; @@ -71,15 +80,21 @@ public abstract class AbstractTcfUITest extends TestCase implements IViewerUpdat protected VariablesVirtualTreeModelViewer fRegistersViewViewer; protected VirtualViewerUpdatesListener fRegistersViewListener; + protected Object fTestRunKey; + protected IDiagnostics diag; protected IExpressions expr; protected ISymbols syms; protected IStackTrace stk; protected IRunControl rc; protected IBreakpoints bp; + protected IMemoryMap fMemoryMap; - protected TestRunControlListener fRcListener; - + protected RunControlCM fRunControlCM; + protected DiagnosticsCM fDiagnosticsCM; + protected BreakpointsCM fBreakpointsCM; + protected StackTraceCM fStackTraceCM; + protected SymbolsCM fSymbolsCM; private static class RemotePeer extends TransientPeer { private final ArrayList<Map<String,String>> attrs; @@ -122,7 +137,9 @@ public abstract class AbstractTcfUITest extends TestCase implements IViewerUpdat } protected void setUp() throws Exception { - + + fTestRunKey = new Object(); + createDebugViewViewer(); createLaunch(); @@ -168,8 +185,6 @@ public abstract class AbstractTcfUITest extends TestCase implements IViewerUpdat getRemoteServices(); - validateTestAvailable(); - new Task<Object>() { @Override public Object call() throws Exception { @@ -177,6 +192,8 @@ public abstract class AbstractTcfUITest extends TestCase implements IViewerUpdat return null; } }.get(); + + validateTestAvailable(); } @Override @@ -198,9 +215,6 @@ public abstract class AbstractTcfUITest extends TestCase implements IViewerUpdat closeChannels(callback); } }.get(); - - // Check for listener errors at the end of tearDown. - fRcListener.checkError(); } protected String getDiagnosticsTestName() { @@ -208,11 +222,19 @@ public abstract class AbstractTcfUITest extends TestCase implements IViewerUpdat } protected void setUpServiceListeners() throws Exception{ - fRcListener = new TestRunControlListener(rc); + fRunControlCM = new RunControlCM(rc); + fDiagnosticsCM = new DiagnosticsCM(diag); + fBreakpointsCM = new BreakpointsCM(bp); + fStackTraceCM = new StackTraceCM(stk, rc); + fSymbolsCM = new SymbolsCM(syms, fRunControlCM, fMemoryMap); } protected void tearDownServiceListeners() throws Exception{ - fRcListener.dispose(); + fSymbolsCM.dispose(); + fBreakpointsCM.dispose(); + fStackTraceCM.dispose(); + fRunControlCM.dispose(); + fDiagnosticsCM.dispose(); } private void createDebugViewViewer() { @@ -248,13 +270,19 @@ public abstract class AbstractTcfUITest extends TestCase implements IViewerUpdat } - private void createLaunch() throws CoreException { + private void createLaunch() throws Exception { ILaunchManager lManager = DebugPlugin.getDefault().getLaunchManager(); ILaunchConfigurationType lcType = lManager.getLaunchConfigurationType("org.eclipse.tcf.debug.LaunchConfigurationType"); ILaunchConfigurationWorkingCopy lcWc = lcType.newInstance(null, "test"); lcWc.doSave(); + fDebugViewListener.reset(); + fDebugViewListener.setDelayContentUntilProxyInstall(true); fLaunch = lcWc.launch("debug", new NullProgressMonitor()); + fDebugViewListener.waitTillFinished(MODEL_CHANGED_COMPLETE | MODEL_PROXIES_INSTALLED | CONTENT_SEQUENCE_COMPLETE | LABEL_UPDATES_RUNNING); Assert.assertTrue( fLaunch instanceof IDisconnect ); + + VirtualItem launchItem = fDebugViewListener.findElement(new Pattern[] { Pattern.compile(".*" + fLaunch.getLaunchConfiguration().getName() + ".*") } ); + Assert.assertTrue( launchItem != null && fLaunch.equals(launchItem.getData()) ); } private void terminateLaunch() throws DebugException, InterruptedException, ExecutionException { @@ -299,6 +327,7 @@ public abstract class AbstractTcfUITest extends TestCase implements IViewerUpdat stk = channels[0].getRemoteService(IStackTrace.class); rc = channels[0].getRemoteService(IRunControl.class); bp = channels[0].getRemoteService(IBreakpoints.class); + fMemoryMap = channels[0].getRemoteService(IMemoryMap.class); }; }); } @@ -430,7 +459,11 @@ public abstract class AbstractTcfUITest extends TestCase implements IViewerUpdat } private void validateTestAvailable() throws ExecutionException, InterruptedException { - String[] testList = getDiagnosticsTestList(); + String[] testList = new Transaction<String[]>() { + protected String[] process() throws InvalidCacheException ,ExecutionException { + return validate( fDiagnosticsCM.getTestList() ); + } + }.get(); int i = 0; for (; i < testList.length; i++) { @@ -440,166 +473,16 @@ public abstract class AbstractTcfUITest extends TestCase implements IViewerUpdat Assert.assertTrue("Required test not supported", i != testList.length); } - protected String[] getDiagnosticsTestList() throws ExecutionException, InterruptedException { - assert !Protocol.isDispatchThread(); - return new Query<String[]>() { + protected ContextState resumeAndWaitForSuspend(final RunControlContext context, final int mode) throws InterruptedException, ExecutionException { + return new Transaction<ContextState>() { @Override - protected void execute(final DataCallback<String[]> callback) { - diag.getTestList(new IDiagnostics.DoneGetTestList() { - public void doneGetTestList(IToken token, Throwable error, String[] list) { - callback.setData(list); - callback.setError(error); - callback.done(); - } - }); - + protected ContextState process() throws InvalidCacheException, ExecutionException { + ICache<Object> waitCache = fRunControlCM.waitForContextSuspended(context.getID(), this); + validate( fRunControlCM.resume(context, this, mode, 1) ); + validate(waitCache); + return validate( fRunControlCM.getState(context.getID()) ); } }.get(); } - protected void setBreakpoint(final String bp_id, final String location) throws InterruptedException, ExecutionException { - new Query<Object> () { - protected void execute(final DataCallback<Object> callback) { - Map<String,Object> m = new HashMap<String,Object>(); - m.put(IBreakpoints.PROP_ID, bp_id); - m.put(IBreakpoints.PROP_ENABLED, Boolean.TRUE); - m.put(IBreakpoints.PROP_LOCATION, location); - bp.set(new Map[]{ m }, new IBreakpoints.DoneCommand() { - public void doneCommand(IToken token, Exception error) { - callback.setError(error); - callback.done(); - } - }); - } - }.get(); - } - - protected String startDiagnosticsTest() throws InterruptedException, ExecutionException { - return new Query<String> () { - protected void execute(final DataCallback<String> callback) { - diag.runTest(getDiagnosticsTestName(), new IDiagnostics.DoneRunTest() { - public void doneRunTest(IToken token, Throwable error, String id) { - callback.setData(id); - callback.setError(error); - callback.done(); - } - }); - } - }.get(); - } - - protected RunControlContext getRunControlContext(final String contextId) throws InterruptedException, ExecutionException { - return new Query<RunControlContext> () { - @Override - protected void execute(final DataCallback<RunControlContext> callback) { - rc.getContext(contextId, new IRunControl.DoneGetContext() { - public void doneGetContext(IToken token, Exception error, IRunControl.RunControlContext ctx) { - callback.setData(ctx); - callback.setError(error); - callback.done(); - } - }); - } - }.get(); - } - - protected String getProcessIdFromRunControlContext(final RunControlContext rcContext) throws InterruptedException, ExecutionException { - return new Task<String>() { - @Override - public String call() throws Exception { - return rcContext.getProcessID(); - } - }.get(); - } - - protected String getSingleThreadIdFromProcess(final String processId) throws InterruptedException, ExecutionException { - return new Query<String> () { - protected void execute(final DataCallback<String> callback) { - rc.getChildren(processId, new IRunControl.DoneGetChildren() { - public void doneGetChildren(IToken token, Exception error, String[] ids) { - if (error != null) { - callback.setError(error); - } - else if (ids == null || ids.length == 0) { - callback.setError(new Exception("Test process has no threads")); - } - else if (ids.length != 1) { - callback.setError(new Exception("Test process has too many threads")); - } - else { - callback.setData(ids[0]); - } - callback.done(); - } - }); - } - }.get(); - } - - protected boolean getRunControlContextHasState(final RunControlContext rcContext) throws InterruptedException, ExecutionException { - return new Task<Boolean>() { - @Override - public Boolean call() throws Exception { - return rcContext.hasState(); - } - }.get(); - } - - protected ISymbol getDiagnosticsSymbol(final String processId, final String testFunction) throws InterruptedException, ExecutionException { - return new Query<ISymbol>() { - @Override - protected void execute(final DataCallback<ISymbol> callback) { - diag.getSymbol(processId, testFunction, new IDiagnostics.DoneGetSymbol() { - public void doneGetSymbol(IToken token, Throwable error, IDiagnostics.ISymbol symbol) { - if (error != null) { - callback.setError(error); - } - else if (symbol == null) { - callback.setError(new Exception("Symbol must not be null: tcf_test_func3")); - } - else { - callback.setData(symbol); - } - callback.done(); - } - }); - } - }.get(); - } - - protected Number getSymbolValue(final ISymbol symbol) throws InterruptedException, ExecutionException { - return new Task<Number>() { - @Override - public Number call() throws Exception { - return symbol.getValue(); - } - }.get(); - } - - protected void resumeContext(final RunControlContext rcContext, final int mode) throws InterruptedException, ExecutionException { - new Query<Object>() { - @Override - protected void execute(final DataCallback<Object> callback) { - rcContext.resume(mode, 1, new IRunControl.DoneCommand() { - public void doneCommand(IToken token, Exception error) { - callback.setError(error); - callback.done(); - } - }); - } - }.get(); - } - - protected void resumeAndWaitForSuspend(final RunControlContext rcContext, final int mode) throws InterruptedException, ExecutionException { - Query<String> suspendQuery = new Query<String> () { - @Override - protected void execute(DataCallback<String> callback) { - fRcListener.addWaitingForSuspend(rcContext.getID(), callback); - } - }; - suspendQuery.invoke(); - resumeContext(rcContext, mode); - suspendQuery.get(); - } - } diff --git a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/Activator.java b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/Activator.java index 5f6ce2de6..35dca5f16 100644 --- a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/Activator.java +++ b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/Activator.java @@ -9,7 +9,7 @@ import org.osgi.framework.BundleContext; public class Activator extends AbstractUIPlugin { // The plug-in ID - public static final String PLUGIN_ID = "org.eclipse.tcf.debug.test"; //$NON-NLS-1$ + public static final String PLUGIN_ID = "org.eclipse.tm.tcf.debug.test"; //$NON-NLS-1$ // The shared instance private static Activator plugin; diff --git a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/BreakpointsTest.java b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/BreakpointsTest.java new file mode 100644 index 000000000..9594baad5 --- /dev/null +++ b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/BreakpointsTest.java @@ -0,0 +1,115 @@ +package org.eclipse.tcf.debug.test; + +import java.util.Collection; +import java.util.Map; +import java.util.TreeMap; +import java.util.concurrent.ExecutionException; + +import org.eclipse.cdt.debug.core.CDIDebugModel; +import org.eclipse.tcf.debug.test.util.Transaction; +import org.eclipse.tcf.services.IBreakpoints; +import org.eclipse.tcf.services.IRunControl.RunControlContext; +import org.junit.Assert; + +@SuppressWarnings("restriction") +public class BreakpointsTest extends AbstractTcfUITest +{ + private String fTestId; + private RunControlContext fTestCtx; + private String fProcessId = ""; + private String fThreadId = ""; + private RunControlContext fThreadCtx; + + private void createBreakpoint(final String bpId, final String testFunc) throws InterruptedException, ExecutionException { + new Transaction<Object>() { + private Map<String,Object> fBp; + + { + fBp = new TreeMap<String,Object>(); + fBp.put(IBreakpoints.PROP_ID, bpId); + fBp.put(IBreakpoints.PROP_ENABLED, Boolean.TRUE); + fBp.put(IBreakpoints.PROP_LOCATION, testFunc); + } + + @Override + protected Object process() throws InvalidCacheException, ExecutionException { + @SuppressWarnings("unchecked") + Map<String, Object>[] bps = (Map<String, Object>[])new Map[] { fBp }; + validate( fBreakpointsCM.set(bps, this) ); + + return null; + } + }.get(); + } + + private void checkBreakpointForErrors(final String bpId, final String processId) throws InterruptedException, ExecutionException { + new Transaction<Object>() { + @Override + protected Object process() throws InvalidCacheException, ExecutionException { + // Wait for breakpoint status event and validate it. + Map<String, Object> status = validate( fBreakpointsCM.getStatus(bpId) ); + String s = (String)status.get(IBreakpoints.STATUS_ERROR); + if (s != null) { + Assert.fail("Invalid BP status: " + s); + } + @SuppressWarnings("unchecked") + Collection<Map<String,Object>> list = (Collection<Map<String,Object>>)status.get(IBreakpoints.STATUS_INSTANCES); + if (list != null) { + String err = null; + for (Map<String,Object> map : list) { + String ctx = (String)map.get(IBreakpoints.INSTANCE_CONTEXT); + if (processId.equals(ctx) && map.get(IBreakpoints.INSTANCE_ERROR) != null) + err = (String)map.get(IBreakpoints.INSTANCE_ERROR); + } + if (err != null) { + Assert.fail("Invalid BP status: " + s); + } + } + return null; + } + }.get(); + } + + private void startProcess() throws InterruptedException, ExecutionException { + new Transaction<Object>() { + protected Object process() throws Transaction.InvalidCacheException ,ExecutionException { + fTestId = validate( fDiagnosticsCM.runTest(getDiagnosticsTestName(), this) ); + fTestCtx = validate( fRunControlCM.getContext(fTestId) ); + fProcessId = fTestCtx.getProcessID(); + // Create the cache to listen for exceptions. + fRunControlCM.waitForContextException(fTestId, fTestRunKey); + + if (!fProcessId.equals(fTestId)) { + fThreadId = fTestId; + } else { + String[] threads = validate( fRunControlCM.getChildren(fProcessId) ); + fThreadId = threads[0]; + } + fThreadCtx = validate( fRunControlCM.getContext(fThreadId) ); + + Assert.assertTrue("Invalid thread context", fThreadCtx.hasState()); + return new Object(); + }; + }.get(); + } + + + private void initProcessModel(String bpId, String testFunc) throws Exception { + createBreakpoint(bpId, testFunc); + fDebugViewListener.reset(); + + startProcess(); + fDebugViewListener.waitTillFinished(MODEL_CHANGED_COMPLETE | CONTENT_SEQUENCE_COMPLETE | LABEL_SEQUENCE_COMPLETE | LABEL_UPDATES); + } + + public void testCreateBreakpoint() throws Exception { + String bpId = "TestStepBP"; + initProcessModel(bpId, "tcf_test_func0"); + + + + //CDIDebugModel.createFunctionBreakpoint(); + + checkBreakpointForErrors(bpId, fProcessId); + } +} diff --git a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/SampleTest.java b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/SampleTest.java index f370b43df..7173f262a 100644 --- a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/SampleTest.java +++ b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/SampleTest.java @@ -1,114 +1,239 @@ package org.eclipse.tcf.debug.test; import java.math.BigInteger; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; import java.util.concurrent.ExecutionException; import java.util.regex.Pattern; import org.eclipse.debug.internal.ui.viewers.model.provisional.VirtualItem; -import org.eclipse.tcf.debug.test.util.DataCallback; -import org.eclipse.tcf.debug.test.util.Query; -import org.eclipse.tcf.debug.test.util.Task; +import org.eclipse.jface.viewers.TreePath; +import org.eclipse.tcf.debug.test.services.IWaitForEventCache; +import org.eclipse.tcf.debug.test.services.RunControlCM; +import org.eclipse.tcf.debug.test.services.RunControlCM.ContextState; +import org.eclipse.tcf.debug.test.util.Transaction; +import org.eclipse.tcf.debug.ui.ITCFObject; +import org.eclipse.tcf.protocol.IChannel; +import org.eclipse.tcf.services.IBreakpoints; import org.eclipse.tcf.services.IDiagnostics.ISymbol; import org.eclipse.tcf.services.IRunControl; import org.eclipse.tcf.services.IRunControl.RunControlContext; +import org.eclipse.tcf.services.ISymbols; +import org.eclipse.tcf.services.ISymbols.Symbol; import org.junit.Assert; @SuppressWarnings("restriction") public class SampleTest extends AbstractTcfUITest { - - private TestBreakpointsListener fBpListener; private String fTestId; private RunControlContext fTestCtx; - private String fProcessId; - private String fThreadId; + private String fProcessId = ""; + private String fThreadId = ""; private RunControlContext fThreadCtx; - - @Override - protected void setUpServiceListeners() throws Exception { - super.setUpServiceListeners(); - fBpListener = new TestBreakpointsListener(bp); - } @Override - protected void tearDownServiceListeners() throws Exception { - fBpListener.dispose(); - super.tearDownServiceListeners(); + protected void setUp() throws Exception { + super.setUp(); + clearBreakpoints(); } - private void createBreakpoint(String bpId, String testFunc) throws InterruptedException, ExecutionException { - fBpListener.setBreakpointId(bpId); - setBreakpoint(bpId, testFunc); - } - - private void startProcess() throws InterruptedException, ExecutionException { - fTestId = startDiagnosticsTest(); - fTestCtx = getRunControlContext(fTestId); - fProcessId = getProcessIdFromRunControlContext(fTestCtx); - fBpListener.setProcess(fProcessId); - fThreadId = null; - if (!fProcessId.equals(fTestId)) { - fThreadId = fTestId; - } else { - fThreadId = getSingleThreadIdFromProcess(fProcessId); - } - fThreadCtx = getRunControlContext(fThreadId); - Assert.assertTrue("Invalid thread context", getRunControlContextHasState(fThreadCtx)); + private void clearBreakpoints() throws InterruptedException, ExecutionException { + new Transaction<Object>() { + @Override + protected Object process() throws InvalidCacheException, ExecutionException { + // Initialize the event cache for breakpoint status + @SuppressWarnings("unchecked") + Map<String, Object>[] bps = (Map<String, Object>[])new Map[] { }; + validate( fBreakpointsCM.set(bps, this) ); + return null; + } + }.get(); } - protected String getProcessNameFromRunControlContext(final RunControlContext rcContext) throws InterruptedException, ExecutionException { - return new Task<String>() { + private void createBreakpoint(final String bpId, final String testFunc) throws InterruptedException, ExecutionException { + new Transaction<Object>() { + private Map<String,Object> fBp; + + { + fBp = new TreeMap<String,Object>(); + fBp.put(IBreakpoints.PROP_ID, bpId); + fBp.put(IBreakpoints.PROP_ENABLED, Boolean.TRUE); + fBp.put(IBreakpoints.PROP_LOCATION, testFunc); + } + @Override - public String call() throws Exception { - return rcContext.getName(); + protected Object process() throws InvalidCacheException, ExecutionException { + + // Initialize the event cache for breakpoint status +// ICache<Map<String, Object>> waitStatusCache = fBreakpointsCM.waitStatusChanged(bpId, fTestRunKey); + + validate( fBreakpointsCM.add(fBp, this) ); + +// // Wait for breakpoint status event and validate it. +// Map<String, Object> status = validate(waitStatusCache); +// String s = (String)status.get(IBreakpoints.STATUS_ERROR); +// if (s != null) { +// Assert.fail("Invalid BP status: " + s); +// } +// @SuppressWarnings("unchecked") +// Collection<Map<String,Object>> list = (Collection<Map<String,Object>>)status.get(IBreakpoints.STATUS_INSTANCES); +// if (list != null) { +// String err = null; +// for (Map<String,Object> map : list) { +// String ctx = (String)map.get(IBreakpoints.INSTANCE_CONTEXT); +// if (processId.equals(ctx) && map.get(IBreakpoints.INSTANCE_ERROR) != null) +// err = (String)map.get(IBreakpoints.INSTANCE_ERROR); +// } +// if (err != null) { +// Assert.fail("Invalid BP status: " + s); +// } +// } + return null; } }.get(); } - - private void runToTestEntry(String testFunc) throws InterruptedException, ExecutionException { - final String suspended_pc = new Query<String> () { + + private void checkBreakpointForErrors(final String bpId, final String processId) throws InterruptedException, ExecutionException { + new Transaction<Object>() { @Override - protected void execute(DataCallback<String> callback) { - fRcListener.waitForSuspend(fThreadCtx, callback); + protected Object process() throws InvalidCacheException, ExecutionException { + // Wait for breakpoint status event and validate it. + Map<String, Object> status = validate( fBreakpointsCM.getStatus(bpId) ); + String s = (String)status.get(IBreakpoints.STATUS_ERROR); + if (s != null) { + Assert.fail("Invalid BP status: " + s); + } + @SuppressWarnings("unchecked") + Collection<Map<String,Object>> list = (Collection<Map<String,Object>>)status.get(IBreakpoints.STATUS_INSTANCES); + if (list != null) { + String err = null; + for (Map<String,Object> map : list) { + String ctx = (String)map.get(IBreakpoints.INSTANCE_CONTEXT); + if (processId.equals(ctx) && map.get(IBreakpoints.INSTANCE_ERROR) != null) + err = (String)map.get(IBreakpoints.INSTANCE_ERROR); + } + if (err != null) { + Assert.fail("Invalid BP status: " + s); + } + } + return null; + } + }.get(); + } + + private void startProcess() throws InterruptedException, ExecutionException { + new Transaction<Object>() { + protected Object process() throws Transaction.InvalidCacheException ,ExecutionException { + fTestId = validate( fDiagnosticsCM.runTest(getDiagnosticsTestName(), this) ); + fTestCtx = validate( fRunControlCM.getContext(fTestId) ); + fProcessId = fTestCtx.getProcessID(); + // Create the cache to listen for exceptions. + fRunControlCM.waitForContextException(fTestId, fTestRunKey); + + if (!fProcessId.equals(fTestId)) { + fThreadId = fTestId; + } else { + String[] threads = validate( fRunControlCM.getChildren(fProcessId) ); + fThreadId = threads[0]; + } + fThreadCtx = validate( fRunControlCM.getContext(fThreadId) ); + + Assert.assertTrue("Invalid thread context", fThreadCtx.hasState()); + return new Object(); + }; + }.get(); + } + + private boolean runToTestEntry(final String testFunc) throws InterruptedException, ExecutionException { + return new Transaction<Boolean>() { + Object fWaitForSuspendKey = new Object(); + boolean fSuspendEventReceived = false; + protected Boolean process() throws Transaction.InvalidCacheException ,ExecutionException { + ISymbol sym_func0 = validate( fDiagnosticsCM.getSymbol(fProcessId, testFunc) ); + String sym_func0_value = sym_func0.getValue().toString(); + ContextState state = validate (fRunControlCM.getState(fThreadId)); + if (state.suspended) { + if ( !new BigInteger(state.pc).equals(new BigInteger(sym_func0_value)) ) { + fSuspendEventReceived = true; + // We are not at test entry. Create a new suspend wait cache. + fWaitForSuspendKey = new Object(); + fRunControlCM.waitForContextSuspended(fThreadId, fWaitForSuspendKey); + // Run to entry point. + validate( fRunControlCM.resume(fThreadCtx, fWaitForSuspendKey, IRunControl.RM_RESUME, 1) ); + } + } else { + // Wait until we suspend. + validate( fRunControlCM.waitForContextSuspended(fThreadId, fWaitForSuspendKey) ); + } + + return fSuspendEventReceived; } }.get(); - ISymbol sym_func0 = getDiagnosticsSymbol(fProcessId, testFunc); - String sym_func0_value = getSymbolValue(sym_func0).toString(); - if (!new BigInteger(sym_func0_value).equals(new BigInteger(suspended_pc))) { - resumeAndWaitForSuspend(fThreadCtx, IRunControl.RM_RESUME); - } } private void initProcessModel(String bpId, String testFunc) throws Exception { createBreakpoint(bpId, testFunc); - fDebugViewListener.reset(); - fDebugViewListener.setDelayContentUntilProxyInstall(true); - startProcess(); - fDebugViewListener.waitTillFinished(MODEL_CHANGED_COMPLETE | MODEL_PROXIES_INSTALLED | CONTENT_SEQUENCE_COMPLETE | LABEL_UPDATES_RUNNING); + + ITCFObject processTCFContext = new ITCFObject() { + public String getID() { return fProcessId; } + public IChannel getChannel() { return channels[0]; } + }; + ITCFObject threadTCFContext = new ITCFObject() { + public String getID() { return fThreadId; } + public IChannel getChannel() { return channels[0]; } + }; + + fDebugViewListener.addLabelUpdate(new TreePath(new Object[] { fLaunch, processTCFContext })); + fDebugViewListener.addLabelUpdate(new TreePath(new Object[] { fLaunch, processTCFContext, threadTCFContext })); + + startProcess(); runToTestEntry(testFunc); + + final String topFrameId = new Transaction<String>() { + @Override + protected String process() throws InvalidCacheException, ExecutionException { + String[] frameIds = validate( fStackTraceCM.getChildren(fThreadId) ); + Assert.assertTrue("No stack frames" , frameIds.length != 0); + return frameIds[frameIds.length - 1]; + } + }.get(); + + ITCFObject frameTCFContext = new ITCFObject() { + public String getID() { return topFrameId; } + public IChannel getChannel() { return channels[0]; } + }; + fDebugViewListener.addLabelUpdate(new TreePath(new Object[] { fLaunch, processTCFContext, threadTCFContext, frameTCFContext })); + + fDebugViewListener.waitTillFinished(MODEL_CHANGED_COMPLETE | CONTENT_SEQUENCE_COMPLETE | LABEL_SEQUENCE_COMPLETE | LABEL_UPDATES); } public void testDebugViewContent() throws Exception { - initProcessModel("TestStepBP", "tcf_test_func0"); - + String bpId = "TestStepBP"; + initProcessModel(bpId, "tcf_test_func0"); + VirtualItem launchItem = fDebugViewListener.findElement(new Pattern[] { Pattern.compile(".*" + fLaunch.getLaunchConfiguration().getName() + ".*") } ); Assert.assertTrue(launchItem != null); - VirtualItem processItem = fDebugViewListener.findElement(launchItem, new Pattern[] { Pattern.compile(".*") } ); + VirtualItem processItem = fDebugViewListener.findElement(launchItem, new Pattern[] { Pattern.compile(".*agent.*") } ); Assert.assertTrue(processItem != null); VirtualItem threadItem = fDebugViewListener.findElement(processItem, new Pattern[] { Pattern.compile(".*" + fThreadId + ".*") } ); Assert.assertTrue(threadItem != null); - VirtualItem frameItem = fDebugViewListener.findElement(threadItem, new Pattern[] { Pattern.compile(".*")}); + VirtualItem frameItem = fDebugViewListener.findElement(threadItem, new Pattern[] { Pattern.compile(".*tcf_test_func0.*")}); Assert.assertTrue(frameItem != null); - fBpListener.checkError(); + checkBreakpointForErrors(bpId, fProcessId); } public void testSteppingDebugViewOnly() throws Exception { - initProcessModel("TestStepBP", "tcf_test_func0"); + String bpId = "TestStepBP"; + initProcessModel(bpId, "tcf_test_func0"); // Execute step loop String previousThreadLabel = null; @@ -126,7 +251,7 @@ public class SampleTest extends AbstractTcfUITest previousThreadLabel = topFrameLabel; } - fBpListener.checkError(); + checkBreakpointForErrors(bpId, fProcessId); } public void testSteppingWithVariablesAndRegisters() throws Exception { @@ -155,8 +280,223 @@ public class SampleTest extends AbstractTcfUITest previousThreadLabel = topFrameLabel; } - fBpListener.checkError(); + checkBreakpointForErrors("TestStepBP", fProcessId); + } + + public void testSymbolsCMResetOnContextRemove() throws Exception { + createBreakpoint("TestStepBP", "tcf_test_func0"); + startProcess(); + runToTestEntry("tcf_test_func0"); + + // Retrieve the current PC for use later + final String pc = new Transaction<String>() { + @Override + protected String process() throws InvalidCacheException, ExecutionException { + return validate(fRunControlCM.getState(fThreadId)).pc; + } + }.get(); + + // Find symbol by name and valide the cache. + final String symbolId = new Transaction<String>() { + @Override + protected String process() throws InvalidCacheException, ExecutionException { + String symId = validate( fSymbolsCM.find(fProcessId, new BigInteger(pc), "tcf_test_func0") ); + Symbol sym = validate( fSymbolsCM.getContext(symId) ); + Assert.assertEquals(ISymbols.UPDATE_ON_MEMORY_MAP_CHANGES, sym.getUpdatePolicy()); + return symId; + } + }.get(); + + // Find symbol by address and validate its context. Save address for later. + final Number symAddr = new Transaction<Number>() { + @Override + protected Number process() throws InvalidCacheException, ExecutionException { + Symbol sym = validate( fSymbolsCM.getContext(symbolId) ); + String symId2 = validate( fSymbolsCM.findByAddr(fProcessId, sym.getAddress()) ); + Symbol sym2 = validate( fSymbolsCM.getContext(symId2) ); + Assert.assertEquals(sym.getAddress(), sym2.getAddress()); + return sym.getAddress(); + } + }.get(); + + // End test, check that all caches were reset and now return an error. + new Transaction<String>() { + @Override + protected String process() throws InvalidCacheException, ExecutionException { + validate( fDiagnosticsCM.cancelTest(fTestId, this) ); + validate( fRunControlCM.waitForContextRemoved(fProcessId, this) ); + try { + validate( fSymbolsCM.getContext(symbolId) ); + Assert.fail("Expected error"); + } catch (ExecutionException e) {} + try { + validate( fSymbolsCM.find(fProcessId, new BigInteger(pc), "tcf_test_func0") ); + Assert.fail("Expected error"); + } catch (ExecutionException e) {} + try { + validate( fSymbolsCM.findByAddr(fProcessId, symAddr) ); + Assert.fail("Expected error"); + } catch (ExecutionException e) {} + + return null; + } + }.get(); + } + + public void testSymbolsCMResetOnContextStateChange() throws Exception { + createBreakpoint("TestStepBP", "tcf_test_func2"); + startProcess(); + runToTestEntry("tcf_test_func2"); + + // Retrieve the current PC and top frame for use later + final String pc = new Transaction<String>() { + @Override + protected String process() throws InvalidCacheException, ExecutionException { + return validate(fRunControlCM.getState(fThreadId)).pc; + } + }.get(); + final String topFrameId = new Transaction<String>() { + @Override + protected String process() throws InvalidCacheException, ExecutionException { + String[] frameIds = validate( fStackTraceCM.getChildren(fThreadId) ); + return frameIds[frameIds.length - 1]; + } + }.get(); + + // Find symbol by name and valide the cache. + final String symbolId = new Transaction<String>() { + @Override + protected String process() throws InvalidCacheException, ExecutionException { + String symId = validate( fSymbolsCM.find(topFrameId, new BigInteger(pc), "func2_local1") ); + Symbol sym = validate( fSymbolsCM.getContext(symId) ); + Assert.assertEquals(ISymbols.UPDATE_ON_EXE_STATE_CHANGES, sym.getUpdatePolicy()); + return symId; + } + }.get(); + + // Note: findByAddr doesn't seem to work on a local var. +// // Find symbol by address and validate its context. Save address for later. +// final Number symAddr = new Transaction<Number>() { +// @Override +// protected Number process() throws InvalidCacheException, ExecutionException { +// Symbol sym = validate( fSymbolsCM.getContext(symbolId) ); +// String symId2 = validate( fSymbolsCM.findByAddr(topFrameId, sym.getAddress()) ); +// Symbol sym2 = validate( fSymbolsCM.getContext(symId2) ); +// Assert.assertEquals(sym.getAddress(), sym2.getAddress()); +// return sym.getAddress(); +// } +// }.get(); + + // Execute a step. + resumeAndWaitForSuspend(fThreadCtx, IRunControl.RM_STEP_OUT); + + // End test, check that all caches were reset and now return an error. + new Transaction<Object>() { + @Override + protected Object process() throws InvalidCacheException, ExecutionException { + Assert.assertFalse( + "Expected cache to be reset", + fSymbolsCM.getContext(symbolId).isValid()); + Assert.assertFalse( + "Expected cache to be reset", + fSymbolsCM.find(topFrameId, new BigInteger(pc), "func2_local1").isValid() ); + return null; + } + }.get(); } + public void testRunControlCMChildrenInvalidation() throws Exception { + createBreakpoint("BP1", "tcf_test_func0"); + startProcess(); + runToTestEntry("tcf_test_func0"); + + // Wait for each threads to start. + final String[] threads = new Transaction<String[]>() { + List<String> fThreads = new ArrayList<String>(); + @Override + protected String[] process() throws InvalidCacheException, ExecutionException { + IWaitForEventCache<RunControlContext[]> waitCache = fRunControlCM.waitForContextAdded(fProcessId, this); + validate(fRunControlCM.resume(fTestCtx, this, IRunControl.RM_RESUME, 1)); + RunControlContext[] addedContexts = validate(waitCache); + for (RunControlContext addedContext : addedContexts) { + fThreads.add(addedContext.getID()); + } + if (fThreads.size() < 4) { + waitCache.reset(); + validate(waitCache); + } + // Validate children cache + String[] children = validate (fRunControlCM.getChildren(fProcessId)); + Assert.assertTrue( + "Expected children array to contain added ids", + Arrays.asList(children).containsAll(fThreads)); + + return fThreads.toArray(new String[fThreads.size()]); + } + }.get(); + + // Wait for each thread to suspend, update caches + for (final String thread : threads) { + new Transaction<Object>() { + @Override + protected Object process() throws InvalidCacheException, ExecutionException { + RunControlCM.ContextState state = validate(fRunControlCM.getState(thread)); + if (!state.suspended) { + validate( fRunControlCM.waitForContextSuspended(thread, this) ); + } + String symId = validate( fSymbolsCM.find(thread, new BigInteger(state.pc), "tcf_test_func0") ); + Number symAddr = validate( fSymbolsCM.getContext(symId) ).getAddress(); + Assert.assertEquals("Expected thread to suspend at breakpoint address", symAddr.toString(), state.pc); + String[] children = validate( fRunControlCM.getChildren(thread)); + Assert.assertEquals("Expected thread to have no children contexts", 0, children.length); + return null; + } + }.get(); + } + + // End test, check for remvoed events and that state caches were cleared + new Transaction<String>() { + @Override + protected String process() throws InvalidCacheException, ExecutionException { + validate( fDiagnosticsCM.cancelTest(fTestId, this) ); + // Create wait caches + IWaitForEventCache<?>[] waitCaches = new IWaitForEventCache<?>[threads.length]; + for (int i = 0; i < threads.length; i++) { + waitCaches[i] = fRunControlCM.waitForContextRemoved(threads[i], this); + } + validate(fRunControlCM.waitForContextRemoved(fProcessId, this)); + + try { + validate( fRunControlCM.getContext(fProcessId) ); + Assert.fail("Expected error"); + } catch (ExecutionException e) {} + try { + validate( fRunControlCM.getState(fProcessId) ); + Assert.fail("Expected error"); + } catch (ExecutionException e) {} + try { + String children[] = validate( fRunControlCM.getChildren(fProcessId) ); + Assert.assertEquals("Expected no children", 0, children.length); + } catch (ExecutionException e) {} + + for (String thread : threads) { + try { + validate( fRunControlCM.getContext(thread) ); + Assert.fail("Expected error"); + } catch (ExecutionException e) {} + try { + validate( fRunControlCM.getState(thread) ); + Assert.fail("Expected error"); + } catch (ExecutionException e) {} + try { + String children[] = validate( fRunControlCM.getChildren(fProcessId) ); + Assert.assertEquals("Expected no children", 0, children.length); + } catch (ExecutionException e) {} + } + + return null; + } + }.get(); + } } diff --git a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/TestBreakpointsListener.java b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/TestBreakpointsListener.java deleted file mode 100644 index d84512fb2..000000000 --- a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/TestBreakpointsListener.java +++ /dev/null @@ -1,83 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2011 Wind River Systems 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: - * Wind River Systems - initial API and implementation - *******************************************************************************/ -package org.eclipse.tcf.debug.test; - -import java.util.Collection; -import java.util.Map; - -import org.eclipse.tcf.services.IBreakpoints; - -/** - * Listener for breakpoint service events. - */ -public class TestBreakpointsListener implements IBreakpoints.BreakpointsListener{ - - final private IBreakpoints fBreakpoints; - private Throwable fError; - private String bp_id; - private String process_id; - private boolean test_done = false; - - public TestBreakpointsListener(IBreakpoints bp) { - fBreakpoints = bp; - fBreakpoints.addListener(this); - } - - public void dispose() { - test_done = true; - fBreakpoints.removeListener(this); - } - - public void setBreakpointId(String bpId) { - bp_id = bpId; - } - - public void setProcess(String processId) { - process_id = processId; - } - - public void checkError() throws Exception { - if (fError != null) { - throw new Exception(fError); - } - } - - public void exit(Throwable error) { - fError = error; - } - - public void breakpointStatusChanged(String id, Map<String, Object> status) { - if (id.equals(bp_id) && process_id != null && !test_done) { - String s = (String)status.get(IBreakpoints.STATUS_ERROR); - if (s != null) exit(new Exception("Invalid BP status: " + s)); - Collection<Map<String,Object>> list = (Collection<Map<String,Object>>)status.get(IBreakpoints.STATUS_INSTANCES); - if (list == null) return; - String err = null; - for (Map<String,Object> map : list) { - String ctx = (String)map.get(IBreakpoints.INSTANCE_CONTEXT); - if (process_id.equals(ctx) && map.get(IBreakpoints.INSTANCE_ERROR) != null) - err = (String)map.get(IBreakpoints.INSTANCE_ERROR); - } - if (err != null) exit(new Exception("Invalid BP status: " + err)); - } - } - - public void contextAdded(Map<String, Object>[] bps) { - } - - public void contextChanged(Map<String, Object>[] bps) { - } - - public void contextRemoved(String[] ids) { - } - - -} diff --git a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/TestRunControlListener.java b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/TestRunControlListener.java deleted file mode 100644 index fb5b04704..000000000 --- a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/TestRunControlListener.java +++ /dev/null @@ -1,207 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2008, 2011 Wind River Systems, Inc. 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: - * Wind River Systems - initial API and implementation - *******************************************************************************/ -package org.eclipse.tcf.debug.test; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.eclipse.debug.internal.ui.actions.RunContextualLaunchAction; -import org.eclipse.tcf.debug.test.util.DataCallback; -import org.eclipse.tcf.protocol.IErrorReport; -import org.eclipse.tcf.protocol.IToken; -import org.eclipse.tcf.services.IRunControl; -import org.eclipse.tcf.services.IRunControl.RunControlContext; - -/** - * Run control service listener. Allows client to wait for events with a - * callback. - */ -public class TestRunControlListener implements IRunControl.RunControlListener { - - private IRunControl fRunControl; - private Throwable fError; - - private final HashMap<String, IRunControl.RunControlContext> ctx_map = new HashMap<String,IRunControl.RunControlContext>(); - private final HashMap<String, String> fSuspendedPCs = new HashMap<String, String>(); - private Map<String, List<DataCallback<String>>> waiting_suspend = new HashMap<String, List<DataCallback<String>>>(); - private String process_id; - private boolean test_done; - private String test_ctx_id; - - - public TestRunControlListener(IRunControl rc) { - fRunControl = rc; - fRunControl.addListener(this); - } - - public void dispose() throws Exception { - test_done = true; - fRunControl.removeListener(this); - } - - public void checkError() throws Exception { - if (fError != null) { - throw new Exception(fError); - } - } - - public void waitForSuspend(RunControlContext context, DataCallback<String> cb) { - final String contextId = context.getID(); - if (fSuspendedPCs.containsKey(contextId)) { - String pc = fSuspendedPCs.get(contextId); - if (pc != null) { - cb.setData(pc); - cb.done(); - return; - } else { - addWaitingForSuspend(contextId, cb); - } - } else { - getContextState(context, cb); - } - } - - private void getContextState(RunControlContext context, final DataCallback<String> cb) { - final String contextId = context.getID(); - context.getState(new IRunControl.DoneGetState() { - public void doneGetState(IToken token, Exception error, - boolean suspended, String pc, String reason, - Map<String,Object> params) { - if (error != null) { - exit(error); - } - else if (suspended) { - fSuspendedPCs.put(contextId, pc); - cb.setData(pc); - cb.done(); - } - else { - fSuspendedPCs.put(contextId, null); - if (cb != null) { - addWaitingForSuspend(contextId, cb); - } - } - } - }); - } - - public void addWaitingForSuspend(String contextId, DataCallback<String> cb) { - List<DataCallback<String>> waitingList = waiting_suspend.get(contextId); - if (waitingList == null) { - waitingList = new ArrayList<DataCallback<String>>(1); - waiting_suspend.put(contextId, waitingList); - } - waitingList.add(cb); - } - - private void exit(Throwable e) { - fError = e; - } - - public void contextAdded(RunControlContext[] contexts) { - for (IRunControl.RunControlContext ctx : contexts) { - if (ctx_map.get(ctx.getID()) != null) exit(new Error("Invalid 'contextAdded' event")); - ctx_map.put(ctx.getID(), ctx); - } - } - - public void contextChanged(RunControlContext[] contexts) { - for (IRunControl.RunControlContext ctx : contexts) { - if (ctx_map.get(ctx.getID()) == null) return; - ctx_map.put(ctx.getID(), ctx); - } - } - - public void waitSuspended(DataCallback<String> cb) { - - } - - public void contextRemoved(String[] context_ids) { - for (String id : context_ids) { - ctx_map.remove(id); - if (id.equals(test_ctx_id)) { - if (test_done) { -// bp.set(null, new IBreakpoints.DoneCommand() { -// public void doneCommand(IToken token, Exception error) { -// exit(error); -// } -// }); - } - else { - exit(new Exception("Test process exited too soon")); - } - return; - } - } - } - - public void contextSuspended(String contextId, String pc, String reason, Map<String, Object> params) { - if (pc == null || "".equals(pc)) { - RunControlContext context = ctx_map.get(contextId); - if (context != null) { - getContextState(context, null); - } - return; - } - - fSuspendedPCs.put(contextId, pc); - List<DataCallback<String>> waitingList = waiting_suspend.remove(contextId); - if (waitingList != null) { - for (DataCallback<String> cb : waitingList) { - cb.setData(pc); - cb.done(); - } - } - if (test_done) { - IRunControl.RunControlContext ctx = ctx_map.get(contextId); - if (ctx != null && process_id != null && process_id.equals(ctx.getParentID())) { - ctx.resume(IRunControl.RM_RESUME, 1, new IRunControl.DoneCommand() { - public void doneCommand(IToken token, Exception error) { - if (error instanceof IErrorReport) { - int code = ((IErrorReport)error).getErrorCode(); - if (code == IErrorReport.TCF_ERROR_ALREADY_RUNNING) return; - if (code == IErrorReport.TCF_ERROR_INV_CONTEXT) return; - } - if (error != null) exit(error); - } - }); - } - } - } - - public void contextResumed(String context) { - fSuspendedPCs.put(context, null); - } - - public void containerSuspended(String context, String pc, String reason, Map<String, Object> params, - String[] suspended_ids) - { - for (String id : suspended_ids) { - assert id != null; - contextSuspended(id, id.equals(context) ? pc : null, null, null); - } - } - - public void containerResumed(String[] resumed_ids) { - for (String id : resumed_ids) { - assert id != null; - contextResumed(id); - } - } - - public void contextException(String context, String msg) { - exit(new Exception("Context exception: " + msg)); - } - - -} diff --git a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/TestViewerInputService.java b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/TestViewerInputService.java deleted file mode 100644 index 5b21d0dc4..000000000 --- a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/TestViewerInputService.java +++ /dev/null @@ -1,104 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2007, 2009 IBM Corporation 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: - * IBM Corporation - initial API and implementation - * Wind River - Pawel Piech - NPE when closing the Variables view (Bug 213719) - *******************************************************************************/ -package org.eclipse.tcf.debug.test; - -import org.eclipse.debug.internal.ui.viewers.model.provisional.ITreeModelViewer; -import org.eclipse.debug.internal.ui.viewers.model.ViewerAdapterService; -import org.eclipse.debug.internal.ui.viewers.model.ViewerInputUpdate; -import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerInputProvider; -import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerInputRequestor; -import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerInputUpdate; - -/** - * Copied from org.eclipse.debug.internal.ui.viewers.model.provisional - * for customization. - * - * Service to compute a viewer input from a source object - * for a given presentation context. - */ -@SuppressWarnings("restriction") -public class TestViewerInputService { - - /** - * An input object which will yield a null input element. - * - * @since 3.6 - */ - public final static Object NULL_INPUT = new IViewerInputProvider() { - public void update(IViewerInputUpdate update) { - update.setInputElement(null); - update.done(); - } - }; - - // previous update request, cancelled when a new request comes in - private IViewerInputUpdate fPendingUpdate = null; - - private IViewerInputRequestor fRequestor = null; - - private ITreeModelViewer fViewer; - - private IViewerInputRequestor fProxyRequest = new IViewerInputRequestor() { - public void viewerInputComplete(final IViewerInputUpdate update) { - synchronized (TestViewerInputService.this) { - fPendingUpdate = null; - } - fRequestor.viewerInputComplete(update); - } - }; - - /** - * Constructs a viewer input service for the given requester and presentation context. - * - * @param requestor client requesting viewer inputs - * @param context context for which inputs are required - */ - public TestViewerInputService(ITreeModelViewer viewer, IViewerInputRequestor requestor) { - fRequestor = requestor; - fViewer = viewer; - } - - /** - * Resolves a viewer input derived from the given source object. - * Reports the result to the given this service's requester. A requester may be called back - * in the same or thread, or asynchronously in a different thread. Cancels any previous - * incomplete request from this service's requester. - * - * @param source source from which to derive a viewer input - */ - public void resolveViewerInput(Object source) { - IViewerInputProvider provdier = ViewerAdapterService.getInputProvider(source); - synchronized (this) { - // cancel any pending update - if (fPendingUpdate != null) { - fPendingUpdate.cancel(); - } - fPendingUpdate = new ViewerInputUpdate(fViewer.getPresentationContext(), fViewer.getInput(), fProxyRequest, source); - } - if (provdier == null) { - fPendingUpdate.setInputElement(source); - fPendingUpdate.done(); - } else { - provdier.update(fPendingUpdate); - } - } - - /** - * Disposes this viewer input service, canceling any pending jobs. - */ - public synchronized void dispose() { - if (fPendingUpdate != null) { - fPendingUpdate.cancel(); - fPendingUpdate = null; - } - } -} diff --git a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/VariablesVirtualTreeModelViewer.java b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/VariablesVirtualTreeModelViewer.java index 439a34b95..f41afd046 100644 --- a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/VariablesVirtualTreeModelViewer.java +++ b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/VariablesVirtualTreeModelViewer.java @@ -55,7 +55,7 @@ public class VariablesVirtualTreeModelViewer extends VirtualTreeModelViewer impl if (fActive) { setActiveContext(fDebugContextProvider.getActiveContext()); } else { - fInputService.resolveViewerInput(TestViewerInputService.NULL_INPUT); + fInputService.resolveViewerInput(ViewerInputService.NULL_INPUT); } } diff --git a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/ViewerUpdatesListener.java b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/ViewerUpdatesListener.java index 6001fd2ab..c175167bf 100644 --- a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/ViewerUpdatesListener.java +++ b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/ViewerUpdatesListener.java @@ -412,7 +412,7 @@ public class ViewerUpdatesListener fLabelUpdatesRunning.remove(update); fLabelUpdatesCompleted.add(update); fLabelUpdatesCounter--; - if (!fLabelUpdates.remove(update.getElementPath()) && fFailOnRedundantUpdates) { + if (!update.isCanceled() && !fLabelUpdates.remove(update.getElementPath()) && fFailOnRedundantUpdates) { fRedundantUpdates.add(update); } notifyAll(); @@ -443,7 +443,7 @@ public class ViewerUpdatesListener if (!fModelProxyInstalled) { delta.accept(new IModelDeltaVisitor() { public boolean visit(IModelDelta delta, int depth) { - if ((delta.getFlags() & IModelDelta.EXPAND) != 0) { + if ((delta.getFlags() & IModelDelta.INSTALL) != 0) { fModelProxyInstalled = true; return false; } diff --git a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/VirtualViewerUpdatesListener.java b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/VirtualViewerUpdatesListener.java index a06fd6789..845dfd6ef 100644 --- a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/VirtualViewerUpdatesListener.java +++ b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/VirtualViewerUpdatesListener.java @@ -10,15 +10,24 @@ *******************************************************************************/ package org.eclipse.tcf.debug.test; +import java.util.AbstractSet; +import java.util.ArrayList; import java.util.Iterator; +import java.util.List; +import java.util.Set; import java.util.regex.Pattern; +import org.eclipse.debug.core.DebugPlugin; import org.eclipse.debug.internal.ui.viewers.model.provisional.VirtualItem; import org.eclipse.debug.internal.ui.viewers.model.provisional.VirtualTreeModelViewer; +import org.eclipse.jface.viewers.IElementComparer; +import org.eclipse.jface.viewers.TreePath; +import org.eclipse.tcf.debug.ui.ITCFObject; /** * Extends base listener to use virtual viewer capabilities. */ +@SuppressWarnings("restriction") public class VirtualViewerUpdatesListener extends ViewerUpdatesListener { private final VirtualTreeModelViewer fVirtualViewer; @@ -45,6 +54,141 @@ public class VirtualViewerUpdatesListener extends ViewerUpdatesListener { } return item; } + + @Override + protected Set<TreePath> makeTreePathSet() { + return new MatchingSet(); + } - + private final IElementComparer fPatternComparer = new IElementComparer() { + public boolean equals(Object a, Object b) { + Pattern pattern = null; + Object element = null; + if (a instanceof Pattern) { + pattern = (Pattern) a; + element = b; + } else if (b instanceof Pattern) { + pattern = (Pattern) b; + element = a; + } + if (pattern != null) { + return elementMatches(element, pattern); + } + return false; + } + + private boolean elementMatches(Object element, Pattern pattern) { + VirtualItem[] items = fVirtualViewer.findItems(element); + if (items.length >= 0) { + String[] label = (String[])items[0].getData(VirtualItem.LABEL_KEY); + if (label != null && label.length >= 1 && label[0] != null && pattern.matcher(label[0]).matches()) { + return true; + } + } + return false; + } + + public int hashCode(Object element) { + throw new UnsupportedOperationException(); + } + }; + + private final IElementComparer fTCFContextComparer = new IElementComparer() { + public boolean equals(Object a, Object b) { + ITCFObject context = null; + Object element = null; + if (a instanceof ITCFObject) { + context = (ITCFObject) a; + element = b; + } else if (b instanceof ITCFObject) { + context = (ITCFObject) b; + element = a; + } + if (context != null) { + return elementMatches((ITCFObject)DebugPlugin.getAdapter(element, ITCFObject.class), context); + } + return false; + } + + private boolean elementMatches(ITCFObject element, ITCFObject pattern) { + return element != null && element.getID().equals(pattern.getID()); + } + + public int hashCode(Object element) { + throw new UnsupportedOperationException(); + } + }; + + class MatchingSet extends AbstractSet<TreePath> { + List<TreePath> fList = new ArrayList<TreePath>(4); + + @Override + public Iterator<TreePath> iterator() { + return fList.iterator(); + } + + @Override + public int size() { + return fList.size(); + } + + @Override + public boolean add(TreePath o) { + return fList.add(o); + } + + @Override + public void clear() { + fList.clear(); + } + + @Override + public boolean contains(Object o) { + if (o instanceof TreePath) { + return find((TreePath)o) >= 0; + } + return false; + } + + @Override + public boolean remove(Object o) { + if (o instanceof TreePath) { + int index = find((TreePath)o); + if (index >= 0) { + fList.remove(index); + return true; + } + } + return false; + } + + private int find(TreePath path) { + for (int i = 0; i < fList.size(); i++) { + if (matches(fList.get(i), path)) { + return i; + } + } + return -1; + } + + private boolean matches(TreePath patternPath, TreePath elementPath) { + if (patternPath.getSegmentCount() != elementPath.getSegmentCount()) { + return false; + } + + for (int i = 0; i < patternPath.getSegmentCount(); i++) { + Object patternSegment = patternPath.getSegment(i); + Object elementSegment = elementPath.getSegment(i); + if ( fPatternComparer.equals(elementSegment, patternSegment) ) { + continue; + } else if (fTCFContextComparer.equals(elementSegment, patternSegment) ) { + continue; + } else if (patternSegment.equals(elementSegment)) { + continue; + } + return false; + } + return true; + } + } } diff --git a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/AbstractCacheManager.java b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/AbstractCacheManager.java new file mode 100644 index 000000000..909483446 --- /dev/null +++ b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/AbstractCacheManager.java @@ -0,0 +1,43 @@ +package org.eclipse.tcf.debug.test.services; + +import java.util.LinkedHashMap; +import java.util.Map; + +import org.eclipse.tcf.debug.test.util.ICache; + +public class AbstractCacheManager { + + protected Map<Key<?>, ICache<?>> fMap = new LinkedHashMap<Key<?>, ICache<?>>(); + + public AbstractCacheManager() { + super(); + } + + public void dispose() { + } + + protected <V> V getCache(Key<V> key) { + @SuppressWarnings("unchecked") + V cache = (V)fMap.get(key); + return cache; + } + + protected <V> V mapCache(Key<V> key) { + @SuppressWarnings("unchecked") + V cache = (V)fMap.get(key); + if (cache != null) return cache; + cache = key.createCache(); + fMap.put(key, (ICache<?>)cache); + return cache; + } + + protected boolean contains(Object[] elements, Object toFind) { + for (int i = 0; i < elements.length; i++) { + if (toFind.equals(elements[i])) { + return true; + } + } + return false; + } + +}
\ No newline at end of file diff --git a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/BreakpointsCM.java b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/BreakpointsCM.java new file mode 100644 index 000000000..3efade570 --- /dev/null +++ b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/BreakpointsCM.java @@ -0,0 +1,348 @@ +/******************************************************************************* + * Copyright (c) 2011 Wind River Systems 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.tcf.debug.test.services; + +import java.util.Map; + +import org.eclipse.tcf.debug.test.util.ICache; +import org.eclipse.tcf.debug.test.util.TokenCache; +import org.eclipse.tcf.protocol.IToken; +import org.eclipse.tcf.services.IBreakpoints; + +/** + * + */ +public class BreakpointsCM extends AbstractCacheManager implements IBreakpoints.BreakpointsListener { + + private IBreakpoints fService; + + public BreakpointsCM(IBreakpoints service) { + fService = service; + fService.addListener(this); + } + + @Override + public void dispose() { + fService.removeListener(this); + // TODO Auto-generated method stub + super.dispose(); + } + + private abstract static class DoneCommandCache extends TokenCache<Object> implements IBreakpoints.DoneCommand { + public void doneCommand(IToken token, Exception error) { + set(token, null, error); + } + } + + public ICache<Object> set(final Map<String,Object>[] properties, Object clientKey) { + class MyCache extends DoneCommandCache { + @Override + protected IToken retrieveToken() { + return fService.set(properties, this); + } + } + return mapCache( new CommandKey<MyCache>(MyCache.class, clientKey) { + @Override MyCache createCache() { return new MyCache(); } + }); + } + + public ICache<Object> add(final Map<String,Object> properties, Object clientKey) { + class MyCache extends DoneCommandCache { + @Override + protected IToken retrieveToken() { + return fService.add(properties, this); + } + } + return mapCache( new CommandKey<MyCache>(MyCache.class, clientKey) { + @Override MyCache createCache() { return new MyCache(); } + }); + } + + public ICache<Object> change(final Map<String,Object> properties, Object clientKey) { + class MyCache extends DoneCommandCache { + @Override + protected IToken retrieveToken() { + return fService.change(properties, this); + } + } + return mapCache( new CommandKey<MyCache>(MyCache.class, clientKey) { + @Override MyCache createCache() { return new MyCache(); } + }); + } + + public ICache<Object> enable(final String[] ids, Object clientKey) { + class MyCache extends DoneCommandCache { + @Override + protected IToken retrieveToken() { + return fService.enable(ids, this); + } + } + return mapCache( new CommandKey<MyCache>(MyCache.class, clientKey) { + @Override MyCache createCache() { return new MyCache(); } + }); + } + + public ICache<Object> disable(final String[] ids, Object clientKey) { + class MyCache extends DoneCommandCache { + @Override + protected IToken retrieveToken() { + return fService.disable(ids, this); + } + } + return mapCache( new CommandKey<MyCache>(MyCache.class, clientKey) { + @Override MyCache createCache() { return new MyCache(); } + }); + } + + public ICache<Object> remove(final String[] ids, Object clientKey) { + class MyCache extends DoneCommandCache { + @Override + protected IToken retrieveToken() { + return fService.remove(ids, this); + } + } + return mapCache( new CommandKey<MyCache>(MyCache.class, clientKey) { + @Override MyCache createCache() { return new MyCache(); } + }); + } + + private class IDsCache extends TokenCache<String[]> implements IBreakpoints.DoneGetIDs { + @Override + protected IToken retrieveToken() { + return fService.getIDs(this); + } + + public void doneGetIDs(IToken token, Exception error, String[] ids) { + set(token, ids, error); + } + + public void resetIDs() { + // TODO: handle add/remove ids + if (isValid()) reset(); + } + } + + private class IDsCacheKey extends Key<IDsCache> { + public IDsCacheKey() { + super(IDsCache.class); + } + + @Override IDsCache createCache() { return new IDsCache(); } + } + + public ICache<String[]> getIDs() { + return mapCache( new IDsCacheKey() ); + } + + private class PropertiesCache extends TokenCache<Map<String,Object>> implements IBreakpoints.DoneGetProperties { + String fId; + + public PropertiesCache(String id) { + fId = id; + } + + @Override + protected IToken retrieveToken() { + return fService.getProperties(fId, this); + } + + public void doneGetProperties(IToken token, Exception error, Map<String, Object> properties) { + set(token, properties, error); + } + + public void setProperties(Map<String, Object> properties) { + set(null, properties, null); + } + + public void resetProperties() { + if (isValid()) reset(); + } + } + + private class PropertiesCacheKey extends IdKey<PropertiesCache> { + public PropertiesCacheKey(String id) { + super(PropertiesCache.class, id); + } + + @Override PropertiesCache createCache() { return new PropertiesCache(fId); } + }; + + public ICache<Map<String,Object>> getProperties(String id) { + return mapCache( new PropertiesCacheKey(id) ); + } + + private class StatusCache extends TokenCache<Map<String,Object>> implements IBreakpoints.DoneGetStatus { + String fId; + + public StatusCache(String id) { + fId = id; + } + + @Override + protected IToken retrieveToken() { + return fService.getStatus(fId, this); + } + + public void doneGetStatus(IToken token, Exception error, Map<String, Object> status) { + set(token, status, error); + } + + public void setStatus(Map<String, Object> status) { + set(null, status, null); + } + } + + private class StatusCacheKey extends IdKey<StatusCache> { + public StatusCacheKey(String id) { + super(StatusCache.class, id); + } + @Override StatusCache createCache() { return new StatusCache(fId); } + } + + public ICache<Map<String,Object>> getStatus(String id) { + return mapCache( new StatusCacheKey(id) ); + } + + public ICache<Map<String,Object>> getCapabilities(final String id) { + class MyCache extends TokenCache<Map<String,Object>> implements IBreakpoints.DoneGetCapabilities { + @Override + protected IToken retrieveToken() { + return fService.getCapabilities(id, this); + } + public void doneGetCapabilities(IToken token, Exception error, Map<String, Object> capabilities) { + set(token, capabilities, error); + } + } + return mapCache( new IdKey<MyCache>(MyCache.class, id) { + @Override MyCache createCache() { return new MyCache(); } + }); + } + + private class StatusChangedCache extends WaitForEventCache<Map<String, Object>> {} + + public ICache<Map<String, Object>> waitStatusChanged(String id, Object clientKey) { + return mapCache(new IdEventKey<StatusChangedCache>(StatusChangedCache.class, id, clientKey) { + @Override + StatusChangedCache createCache() { + return new StatusChangedCache(); + } + }); + } + + public void breakpointStatusChanged(String id, final Map<String, Object> status) { + StatusCache statusCache = getCache(new StatusCacheKey(id)); + if (statusCache != null) { + statusCache.setStatus(status); + } + + // TODO: avoid iterating over all entries, use separate list for events. + for (Map.Entry<Key<?>, ICache<?>> entry: fMap.entrySet()) { + if (entry.getKey() instanceof IdEventKey) { + IdEventKey<?> eventKey = (IdEventKey<?>)entry.getKey(); + if ( StatusChangedCache.class.equals( eventKey.getCacheClass() ) && + eventKey.fId.equals(id) ) + { + ((StatusChangedCache)entry.getValue()).eventReceived(status); + } + } + } + } + + private void setBreakpointsProperties(Map<String, Object>[] bps) { + for (Map<String, Object> bp : bps) { + Object id = (String)bp.get(IBreakpoints.PROP_ID); + if (id instanceof String) { + PropertiesCache cache = mapCache(new PropertiesCacheKey((String)id)); + cache.setProperties(bp); + } + } + } + + private class ContextAddedCache extends WaitForEventCache<Map<String, Object>[]> {} + + public ICache<Map<String, Object>[]> waitContextAdded(Object clientKey) { + return mapCache(new EventKey<ContextAddedCache>(ContextAddedCache.class, clientKey) { + @Override + ContextAddedCache createCache() { + return new ContextAddedCache(); + } + }); + } + + public void contextAdded(Map<String, Object>[] bps) { + IDsCache idsCache = getCache(new IDsCacheKey()); + if (idsCache != null && idsCache.isValid()) { + idsCache.resetIDs(); + } + + setBreakpointsProperties(bps); + + // TODO: avoid iterating over all entries, use separate list for events. + for (Map.Entry<Key<?>, ICache<?>> entry: fMap.entrySet()) { + if (entry.getKey() instanceof IdEventKey) { + IdEventKey<?> eventKey = (IdEventKey<?>)entry.getKey(); + if ( ContextAddedCache.class.equals( eventKey.getCacheClass() ) ) { + ((ContextAddedCache)entry.getValue()).eventReceived(bps); + } + } + } + } + + private class ContextChangedCache extends WaitForEventCache<Map<String, Object>[]> {} + + public ICache<Map<String, Object>[]> waitContextChanged(Object clientKey) { + return mapCache(new EventKey<ContextChangedCache>(ContextChangedCache.class, clientKey) { + @Override + ContextChangedCache createCache() { + return new ContextChangedCache(); + } + }); + } + + public void contextChanged(Map<String, Object>[] bps) { + setBreakpointsProperties(bps); + + // TODO: avoid iterating over all entries, use separate list for events. + for (Map.Entry<Key<?>, ICache<?>> entry: fMap.entrySet()) { + if (entry.getKey() instanceof IdEventKey) { + IdEventKey<?> eventKey = (IdEventKey<?>)entry.getKey(); + if ( ContextChangedCache.class.equals( eventKey.getCacheClass() ) ) { + ((ContextChangedCache)entry.getValue()).eventReceived(bps); + } + } + } + } + + private class ContextRemovedCache extends WaitForEventCache<String[]> {} + + public ICache<String[]> waitContextRemoved(Object clientKey) { + return mapCache(new EventKey<ContextRemovedCache>(ContextRemovedCache.class, clientKey) { + @Override + ContextRemovedCache createCache() { + return new ContextRemovedCache(); + } + }); + } + + public void contextRemoved(String[] ids) { + IDsCache idsCache = getCache(new IDsCacheKey()); + if (idsCache != null) { + idsCache.resetIDs(); + } + + for (String id : ids) { + PropertiesCache cache = mapCache(new PropertiesCacheKey(id)); + cache.resetProperties(); + } + } + + +} diff --git a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/CommandKey.java b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/CommandKey.java new file mode 100644 index 000000000..51f00c8f0 --- /dev/null +++ b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/CommandKey.java @@ -0,0 +1,37 @@ +/******************************************************************************* + * Copyright (c) 2011 Wind River Systems 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.tcf.debug.test.services; + + +/** + * + */ +public abstract class CommandKey<V> extends Key<V> { + Object fClientKey; + + CommandKey(Class<V> cacheClass, Object clientKey) { + super(cacheClass); + fClientKey = clientKey; + } + + @Override + public boolean equals(Object obj) { + if (super.equals(obj) && obj instanceof CommandKey<?>) { + return ((CommandKey<?>)obj).fClientKey.equals(fClientKey); + } + return false; + } + + @Override + public int hashCode() { + return super.hashCode() + fClientKey.hashCode(); + } +} diff --git a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/DiagnosticsCM.java b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/DiagnosticsCM.java new file mode 100644 index 000000000..c2a257112 --- /dev/null +++ b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/DiagnosticsCM.java @@ -0,0 +1,136 @@ +/******************************************************************************* + * Copyright (c) 2011 Wind River Systems 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.tcf.debug.test.services; + +import org.eclipse.tcf.debug.test.util.ICache; +import org.eclipse.tcf.debug.test.util.TokenCache; +import org.eclipse.tcf.protocol.IToken; +import org.eclipse.tcf.services.IDiagnostics; +import org.eclipse.tcf.services.IDiagnostics.ISymbol; + +/** + * + */ +public class DiagnosticsCM extends AbstractCacheManager{ + private IDiagnostics fService; + + public DiagnosticsCM(IDiagnostics service) { + fService = service; + } + + @Override + public void dispose() { + super.dispose(); + } + + public ICache<String> echo(final String msg, Object clientId) { + class MyCache extends TokenCache<String> implements IDiagnostics.DoneEcho { + @Override + protected IToken retrieveToken() { + return fService.echo(msg, this); + } + public void doneEcho(IToken token, Throwable error, String s) { + set(token, s, error); + } + }; + + return mapCache(new CommandKey<MyCache>(MyCache.class, clientId) { + @Override MyCache createCache() { return new MyCache(); } + }); + } + + public ICache<String[]> getTestList() { + class MyCache extends TokenCache<String[]> implements IDiagnostics.DoneGetTestList { + @Override + protected IToken retrieveToken() { + return fService.getTestList(this); + } + public void doneGetTestList(IToken token, Throwable error, String[] list) { + set(token, list, error); + } + }; + + return mapCache(new Key<MyCache>(MyCache.class) { + @Override MyCache createCache() { return new MyCache(); } + }); + } + + public ICache<String> runTest(final String name, Object clientId) { + class MyCache extends TokenCache<String> implements IDiagnostics.DoneRunTest { + @Override + protected IToken retrieveToken() { + return fService.runTest(name, this); + } + public void doneRunTest(IToken token, Throwable error, String context_id) { + set(token, context_id, error); + } + }; + + return mapCache(new CommandKey<MyCache>(MyCache.class, clientId) { + @Override MyCache createCache() { return new MyCache(); } + }); + } + + public ICache<Object> cancelTest(final String context_id, Object clientId) { + class MyCache extends TokenCache<Object> implements IDiagnostics.DoneCancelTest { + @Override + protected IToken retrieveToken() { + return fService.cancelTest(context_id, this); + } + public void doneCancelTest(IToken token, Throwable error) { + set(token, null, error); + } + }; + + return mapCache(new CommandKey<MyCache>(MyCache.class, clientId) { + @Override MyCache createCache() { return new MyCache(); } + }); + } + + abstract class SymbolKey<V> extends IdKey<V> { + String fSymbolName; + + public SymbolKey(Class<V> clazz, String id, String symbolName) { + super(clazz, id); + fSymbolName = symbolName; + } + + @Override + public boolean equals(Object obj) { + if (super.equals(obj) && obj instanceof SymbolKey<?>) { + return ((SymbolKey<?>)obj).fSymbolName.equals(fSymbolName); + } + return false; + } + + @Override + public int hashCode() { + return super.hashCode() + fSymbolName.hashCode(); + } + } + + public ICache<IDiagnostics.ISymbol> getSymbol(final String context_id, final String symbol_name) { + class MyCache extends TokenCache<IDiagnostics.ISymbol> implements IDiagnostics.DoneGetSymbol { + @Override + protected IToken retrieveToken() { + return fService.getSymbol(context_id, symbol_name, this); + } + public void doneGetSymbol(IToken token, Throwable error, ISymbol symbol) { + set(token, symbol, error); + } + }; + + return mapCache(new SymbolKey<MyCache>(MyCache.class, context_id, symbol_name) { + @Override MyCache createCache() { return new MyCache(); } + }); + } + +} diff --git a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/EventKey.java b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/EventKey.java new file mode 100644 index 000000000..20869ffbf --- /dev/null +++ b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/EventKey.java @@ -0,0 +1,24 @@ +package org.eclipse.tcf.debug.test.services; + + +abstract class EventKey<V> extends Key<V> { + private Object fClientKey; + + public EventKey(Class<V> eventClazz, Object clientKey) { + super(eventClazz); + fClientKey = clientKey; + } + + @Override + public boolean equals(Object obj) { + if (super.equals(obj) && obj instanceof EventKey<?>) { + return ((EventKey<?>)obj).fClientKey.equals(fClientKey); + } + return false; + } + + @Override + public int hashCode() { + return super.hashCode() + fClientKey.hashCode(); + } +}
\ No newline at end of file diff --git a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/IWaitForEventCache.java b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/IWaitForEventCache.java new file mode 100644 index 000000000..50dd8bf01 --- /dev/null +++ b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/IWaitForEventCache.java @@ -0,0 +1,24 @@ +/******************************************************************************* + * Copyright (c) 2012 Wind River Systems 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.tcf.debug.test.services; + +import org.eclipse.tcf.debug.test.util.ICache; + +/** + * + */ +public interface IWaitForEventCache<V> extends ICache<V> { + + /** + * Resets the event cache so that it will be called again upon the next event. + */ + public void reset(); +} diff --git a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/IdEventKey.java b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/IdEventKey.java new file mode 100644 index 000000000..019d7b6a6 --- /dev/null +++ b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/IdEventKey.java @@ -0,0 +1,24 @@ +package org.eclipse.tcf.debug.test.services; + + +abstract class IdEventKey<V> extends IdKey<V> { + private Object fClientKey; + + public IdEventKey(Class<V> eventClazz, String id, Object clientKey) { + super(eventClazz, id); + fClientKey = clientKey; + } + + @Override + public boolean equals(Object obj) { + if (super.equals(obj) && obj instanceof IdEventKey<?>) { + return ((IdEventKey<?>)obj).fClientKey.equals(fClientKey); + } + return false; + } + + @Override + public int hashCode() { + return super.hashCode() + fClientKey.hashCode(); + } +}
\ No newline at end of file diff --git a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/IdKey.java b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/IdKey.java new file mode 100644 index 000000000..2d355dafa --- /dev/null +++ b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/IdKey.java @@ -0,0 +1,28 @@ +package org.eclipse.tcf.debug.test.services; + + +abstract class IdKey<V> extends Key<V> { + String fId; + + public IdKey(Class<V> clazz, String id) { + super(clazz); + fId = id; + } + + public String getId() { + return fId; + } + + @Override + public boolean equals(Object obj) { + if (super.equals(obj) && obj instanceof IdKey<?>) { + return ((IdKey<?>)obj).fId.equals(fId); + } + return false; + } + + @Override + public int hashCode() { + return super.hashCode() + fId.hashCode(); + } +}
\ No newline at end of file diff --git a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/Key.java b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/Key.java new file mode 100644 index 000000000..91aae61d6 --- /dev/null +++ b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/Key.java @@ -0,0 +1,29 @@ +package org.eclipse.tcf.debug.test.services; + + +abstract class Key<V> { + private Class<V> fCacheClass; + + public Key(Class<V> cacheClass) { + fCacheClass = cacheClass; + } + + abstract V createCache(); + + public Class<V> getCacheClass() { + return fCacheClass; + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof Key) { + return ((Key<?>)obj).fCacheClass.equals(fCacheClass); + } + return false; + } + + @Override + public int hashCode() { + return fCacheClass.hashCode(); + } +}
\ No newline at end of file diff --git a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/ResetMap.java b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/ResetMap.java new file mode 100644 index 000000000..56bfc2820 --- /dev/null +++ b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/ResetMap.java @@ -0,0 +1,159 @@ +/******************************************************************************* + * Copyright (c) 2012 Wind River Systems 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.tcf.debug.test.services; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; +import java.util.TreeSet; + +import org.eclipse.tcf.debug.test.util.AbstractCache; +import org.eclipse.tcf.protocol.Protocol; + +/** + * + */ +public class ResetMap { + + public static final String ANY_ID = ""; + + private Map<String, List<AbstractCache<?>>> fValid = new TreeMap<String, List<AbstractCache<?>>>(); + private Map<String, Set<String>> fChildren = new TreeMap<String, Set<String>>(); + private Map<String, String> fParents = new TreeMap<String, String>(); + private Map<AbstractCache<?>, Set<String>> fPending = new LinkedHashMap<AbstractCache<?>, Set<String>>(); + + public synchronized Set<String> removePending(AbstractCache<?> cache) { + Set<String> pendingIds = fPending.remove(cache); + if (pendingIds == null) { + pendingIds = Collections.emptySet(); + } + return pendingIds; + } + + public synchronized void addValid(String id, AbstractCache<?> cache) { + assert !fPending.containsKey(cache); + + List<AbstractCache<?>> list = fValid.get(id); + if (list == null) { + list = new ArrayList<AbstractCache<?>>(); + fValid.put(id, list); + } + list.add(cache); + } + + public synchronized void addValid(String id, String[] childrenIds, AbstractCache<?> cache) { + assert !fPending.containsKey(cache); + + List<AbstractCache<?>> list = fValid.get(id); + if (list == null) { + list = new ArrayList<AbstractCache<?>>(); + fValid.put(id, list); + } + list.add(cache); + for (String childId : childrenIds) { + fParents.put(childId, id); + } + } + + public synchronized void addValid(List<String> ids, AbstractCache<?> cache) { + assert !fPending.containsKey(cache); + + String id = ids.get(0); + List<AbstractCache<?>> list = fValid.get(id); + if (list == null) { + list = new ArrayList<AbstractCache<?>>(); + fValid.put(id, list); + } + list.add(cache); + + for (int i = 0; i < ids.size() - 1; i++) { + Set<String> children = fChildren.get(ids.get(i + 1)); + if (children == null) { + children = new TreeSet<String>(); + fChildren.put(ids.get(i + 1), children); + } + children.add(ids.get(i)); + } + } + + public synchronized List<AbstractCache<?>> getCaches(String id) { + List<AbstractCache<?>> list = fValid.get(id); + if (list == null) { + list = Collections.emptyList(); + } + return list; + } + + public void reset(String id) { + reset(id, true, true); + } + + public void reset(String id, boolean resetChildren, boolean resetParents) { + assert Protocol.isDispatchThread(); + + // Do not call reset while holding lock to reset map. Instead collect + // caches to reset and reset them outside the lock. + List<AbstractCache<?>> anyList = Collections.emptyList(); + List<AbstractCache<?>> idList = Collections.emptyList(); + List<AbstractCache<?>> parentList = Collections.emptyList(); + synchronized (this) { + for (Set<String> pendingIds : fPending.values()) { + pendingIds.add(id); + } + anyList = fValid.remove(ANY_ID); + + if (resetChildren && fChildren.containsKey(id)) { + idList = new ArrayList<AbstractCache<?>>(); + collectChildren(id, idList); + } else { + idList = fValid.remove(id); + } + + if (resetParents) { + String parentId = fParents.remove(id); + if (parentId != null) { + parentList = fValid.remove(parentId); + } + } + } + resetList(anyList); + resetList(idList); + resetList(parentList); + } + + private void collectChildren(String id, List<AbstractCache<?>> caches) { + caches.addAll( fValid.remove(id) ); + Set<String> children = fChildren.remove(id); + if (children != null) { + for (String child : children) { + collectChildren(child, caches); + } + } + } + + private void resetList(List<AbstractCache<?>> list) { + if (list != null) { + for (AbstractCache<?> cache : list) { + if (cache.isValid()) { + cache.reset(); + } + } + } + } + + public synchronized void addPending(AbstractCache<?> cache) { + fPending.put(cache, new TreeSet<String>()); + } +} diff --git a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/RunControlCM.java b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/RunControlCM.java new file mode 100644 index 000000000..ca4b8194e --- /dev/null +++ b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/RunControlCM.java @@ -0,0 +1,629 @@ +/******************************************************************************* + * Copyright (c) 2011 Wind River Systems 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.tcf.debug.test.services; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; +import java.util.concurrent.ExecutionException; + +import org.eclipse.tcf.debug.test.util.CallbackCache; +import org.eclipse.tcf.debug.test.util.DataCallback; +import org.eclipse.tcf.debug.test.util.ICache; +import org.eclipse.tcf.debug.test.util.TokenCache; +import org.eclipse.tcf.debug.test.util.Transaction; +import org.eclipse.tcf.protocol.IToken; +import org.eclipse.tcf.services.IRunControl; +import org.eclipse.tcf.services.IRunControl.DoneCommand; +import org.eclipse.tcf.services.IRunControl.RunControlContext; +import org.eclipse.tcf.services.IRunControl.RunControlListener; + +/** + * + */ +public class RunControlCM extends AbstractCacheManager implements RunControlListener { + + private final IRunControl fService; + private final ResetMap fStateResetMap = new ResetMap(); + private final ResetMap fChildrenResetMap = new ResetMap(); + private final List<RunControlListener> fListeners = new ArrayList<RunControlListener>(); + + public RunControlCM(IRunControl service) { + fService = service; + fService.addListener(this); + } + + public void dispose() { + fService.removeListener(this); + super.dispose(); + } + + public void addListener(RunControlListener listener) { + fListeners.add(listener); + } + + public void removeListener(RunControlListener listener) { + fListeners.remove(listener); + } + + public IRunControl getService() { + return fService; + } + + public ICache<RunControlContext> getContext(final String id) { + class MyCache extends RunControlTokenCache<RunControlContext> implements IRunControl.DoneGetContext { + @Override + protected String getId() { + return id; + } + @Override + protected IToken retrieveToken() { + return fService.getContext(id, this); + } + public void doneGetContext(IToken token, Exception error, RunControlContext context) { + set(token, context, error); + } + + }; + + return mapCache(new IdKey<MyCache>(MyCache.class, id) { + @Override MyCache createCache() { return new MyCache(); } + }); + } + + private abstract class RunControlTokenCache<V> extends TokenCache<V> { + abstract protected String getId(); + + protected void set(IToken token, V data, Throwable error) { + fStateResetMap.addValid(getId(), this); + super.set(token, data, error); + } + } + + private class ChildrenCache extends TokenCache<String[]> implements IRunControl.DoneGetChildren { + private final String fId; + public ChildrenCache(String id) { + fId = id; + } + + @Override + protected IToken retrieveToken() { + return fService.getChildren(fId, this); + } + public void doneGetChildren(IToken token, Exception error, String[] child_ids) { + fChildrenResetMap.addValid(fId, child_ids, this); + set(token, child_ids, error); + } + }; + + private class ChildrenCacheKey extends IdKey<ChildrenCache> { + public ChildrenCacheKey(String id) { + super(ChildrenCache.class, id); + } + @Override ChildrenCache createCache() { return new ChildrenCache(fId); } + } + + public ICache<String[]> getChildren(String id) { + return mapCache(new ChildrenCacheKey(id)); + } + + public static class ContextState { + public final boolean suspended; + public final String pc; + public final String reason; + public final Map<String, Object> params; + ContextState(boolean suspended, String pc, String reason, Map<String, Object> params) { + this.suspended = suspended; + this.pc = pc; + this.reason = reason; + this.params = params; + } + + } + + protected abstract static class ContextKey<V> extends Key<V> { + RunControlContext fContext; + + ContextKey(Class<V> cacheClass, RunControlContext context) { + super(cacheClass); + fContext = context; + } + + @Override + public boolean equals(Object obj) { + if (super.equals(obj) && obj instanceof ContextKey<?>) { + return ((ContextKey<?>)obj).fContext.equals(fContext); + } + return false; + } + + @Override + public int hashCode() { + return super.hashCode() + fContext.hashCode(); + } + } + + + private class ContextStateCache extends CallbackCache<ContextState> { + + private class InnerContextStateCache extends TokenCache<ContextState> implements IRunControl.DoneGetState { + private final RunControlContext fContext; + + public InnerContextStateCache(RunControlContext context) { + fContext = context; + } + + public void doneGetState(IToken token, Exception error, boolean suspended, String pc, String reason, + Map<String, Object> params) { + set(token, new ContextState(suspended, pc, reason, params), error); + } + + @Override + protected IToken retrieveToken() { + return fContext.getState(this); + } + } + + private final String fId; + private InnerContextStateCache fInnerCache; + public ContextStateCache(String id) { + fId = id; + } + + @Override + protected void retrieve(DataCallback<ContextState> rm) { + new Transaction<ContextState>() { + @Override + protected ContextState process() throws InvalidCacheException, ExecutionException + { + RunControlContext context = validate( getContext(fId) ); + if (fInnerCache == null || !fInnerCache.fContext.equals(context)) { + fInnerCache = new InnerContextStateCache(context); + } + return validate(fInnerCache); + } + }.request(rm); + } + + @Override + protected void handleCompleted(ContextState data, Throwable error, boolean canceled) { + if (canceled) return; + fStateResetMap.addValid(fId, this); + set(data, error, true); + } + + public void setState(ContextState state, Throwable error) { + fStateResetMap.addValid(fId, this); + set(state, error, true); + } + + public void reset() { + super.reset(); + if (fInnerCache != null) { + fInnerCache.reset(); + } + } + + } + + private class ContextStateKey extends IdKey<ContextStateCache> { + public ContextStateKey(String id) { + super(ContextStateCache.class, id); + } + @Override + ContextStateCache createCache() { + return new ContextStateCache(getId()); + } + } + + public ICache<ContextState> getState(String id) { + return mapCache(new ContextStateKey(id)); + } + + protected abstract static class ContextCommandKey<V> extends ContextKey<V> { + Object fClientKey; + + ContextCommandKey(Class<V> cacheClass, RunControlContext context, Object clientKey) { + super(cacheClass, context); + fClientKey = clientKey; + } + + @Override + public boolean equals(Object obj) { + if (super.equals(obj) && obj instanceof ContextCommandKey<?>) { + return ((ContextCommandKey<?>)obj).fClientKey.equals(fClientKey); + } + return false; + } + + @Override + public int hashCode() { + return super.hashCode() + fClientKey.hashCode(); + } + } + + private abstract static class DoneCommandCache extends TokenCache<Object> implements DoneCommand { + public void doneCommand(IToken token, Exception error) { + set(token, null, error); + } + } + + public ICache<Object> suspend(final RunControlContext context, Object clientKey) { + class MyCache extends DoneCommandCache { + @Override + protected IToken retrieveToken() { + return context.suspend(this); + } + } + return mapCache( new ContextCommandKey<MyCache>(MyCache.class, context, clientKey) { + @Override MyCache createCache() { return new MyCache(); } + }); + } + + public ICache<Object> resume(final RunControlContext context, Object clientKey, final int mode, + final int count) + { + class MyCache extends DoneCommandCache { + @Override + protected IToken retrieveToken() { + return context.resume(mode, count, this); + } + } + return mapCache( new ContextCommandKey<MyCache>(MyCache.class, context, clientKey) { + @Override MyCache createCache() { return new MyCache(); } + }); + } + + public ICache<Object> resume(final RunControlContext context, Object clientKey, final int mode, + final int count, final Map<String,Object> params) + { + class MyCache extends DoneCommandCache { + @Override + protected IToken retrieveToken() { + return context.resume(mode, count, params, this); + } + } + return mapCache( new ContextCommandKey<MyCache>(MyCache.class, context, clientKey) { + @Override MyCache createCache() { return new MyCache(); } + }); + } + + public ICache<Object> terminate(final RunControlContext context, Object clientKey) { + class MyCache extends DoneCommandCache { + @Override + protected IToken retrieveToken() { + return context.terminate(this); + } + } + return mapCache( new ContextCommandKey<MyCache>(MyCache.class, context, clientKey) { + @Override MyCache createCache() { return new MyCache(); } + }); + + } + + private class WaitForContainerResumedCache extends WaitForEventCache<String[]> {} + + public IWaitForEventCache<String[]> waitForContainerResumed(String id, Object clientKey) { + return mapCache(new IdEventKey<WaitForContainerResumedCache>(WaitForContainerResumedCache.class, id, clientKey) { + @Override + WaitForContainerResumedCache createCache() { + return new WaitForContainerResumedCache(); + } + }); + } + + public void containerResumed(String[] context_ids) { + for (RunControlListener listener : fListeners) { + listener.containerResumed(context_ids); + } + + for (Map.Entry<Key<?>, ICache<?>> entry: fMap.entrySet()) { + if (entry.getKey() instanceof IdEventKey) { + IdEventKey<?> eventKey = (IdEventKey<?>)entry.getKey(); + if ( WaitForContainerResumedCache.class.equals(eventKey.getCacheClass()) && + contains(context_ids, eventKey.fId) ) + { + ((WaitForContainerResumedCache)entry.getValue()).eventReceived(context_ids); + } + } + } + for (String id : context_ids) { + doContextResumed(id); + } + } + + private class WaitForContainerSuspendedCache extends WaitForEventCache<String[]> {} + + public IWaitForEventCache<String[]> waitForContainerSuspended(String id, Object clientKey) { + return mapCache(new IdEventKey<WaitForContainerSuspendedCache>(WaitForContainerSuspendedCache.class, id, clientKey) { + @Override + WaitForContainerSuspendedCache createCache() { + return new WaitForContainerSuspendedCache(); + } + }); + } + + public void containerSuspended(String context, String pc, String reason, Map<String, Object> params, + String[] suspended_ids) + { + // Call client listeners first + for (RunControlListener listener : fListeners) { + listener.containerSuspended(context, pc, reason, params, suspended_ids); + } + + for (Map.Entry<Key<?>, ICache<?>> entry: fMap.entrySet()) { + if (entry.getKey() instanceof IdEventKey) { + IdEventKey<?> eventKey = (IdEventKey<?>)entry.getKey(); + if ( WaitForContainerSuspendedCache.class.equals( eventKey.getCacheClass() ) && + eventKey.fId.equals(context) ) + { + ((WaitForContainerSuspendedCache)entry.getValue()).eventReceived(suspended_ids); + } + } + } + + ContextState state = pc == null ? null : new ContextState(true, pc, reason, params); + doContextSuspended(context, state); + + for (String id : suspended_ids) { + if (!id.equals(context)) { + doContextSuspended(id, null); + } + } + } + + private class WaitForContextSuspendedCache extends WaitForEventCache<Object> {} + + public IWaitForEventCache<Object> waitForContextSuspended(String id, Object clientKey) { + return mapCache(new IdEventKey<WaitForContextSuspendedCache>(WaitForContextSuspendedCache.class, id, clientKey) { + @Override + WaitForContextSuspendedCache createCache() { + return new WaitForContextSuspendedCache(); + } + }); + } + + public void contextSuspended(String id, String pc, String reason, Map<String, Object> params) { + // Call client listeners first + for (RunControlListener listener : fListeners) { + listener.contextSuspended(id, pc, reason, params); + } + + ContextState state = pc == null ? null : new ContextState(true, pc, reason, params); + doContextSuspended(id, state); + } + + public void doContextSuspended(String id, ContextState state) { + fStateResetMap.reset(id); + ContextStateCache stateCache = getCache(new ContextStateKey(id)); + if (stateCache != null) { + if (state != null) { + stateCache.setState(state, null); + } + } + + // TODO: avoid iterating over all entries, use separate list for events. + for (Map.Entry<Key<?>, ICache<?>> entry: fMap.entrySet()) { + if (entry.getKey() instanceof IdEventKey) { + IdEventKey<?> eventKey = (IdEventKey<?>)entry.getKey(); + if ( WaitForContextSuspendedCache.class.equals( eventKey.getCacheClass() ) && + eventKey.fId.equals(id) ) + { + ((WaitForContextSuspendedCache)entry.getValue()).eventReceived(null); + } + } + } + } + + private class WaitForContextResumedCache extends WaitForEventCache<Object> {} + + public IWaitForEventCache<Object> waitForContextResumed(String id, Object clientKey) { + return mapCache(new IdEventKey<WaitForContextResumedCache>(WaitForContextResumedCache.class, id, clientKey) { + @Override + WaitForContextResumedCache createCache() { + return new WaitForContextResumedCache(); + } + }); + } + + private static final ContextState RESUMED_STATE = new ContextState(false, null, null, null); + + public void contextResumed(String id) { + for (RunControlListener listener : fListeners) { + listener.contextResumed(id); + } + doContextResumed(id); + } + + private void doContextResumed(String id) { + for (RunControlListener listener : fListeners) { + listener.contextResumed(id); + } fStateResetMap.reset(id); + + ContextStateCache stateCache = getCache(new ContextStateKey(id)); + if (stateCache != null) { + stateCache.setState(RESUMED_STATE, null); + } + + // TODO: avoid iterating over all entries, use separate list for events. + for (Map.Entry<Key<?>, ICache<?>> entry: fMap.entrySet()) { + if (entry.getKey() instanceof IdEventKey) { + IdEventKey<?> eventKey = (IdEventKey<?>)entry.getKey(); + if ( WaitForContextResumedCache.class.equals( eventKey.getCacheClass() ) && + eventKey.fId.equals(id) ) + { + ((WaitForContextResumedCache)entry.getValue()).eventReceived(null); + } + } + } + } + + private class WaitForContextExceptionCache extends WaitForEventCache<String> {} + + public IWaitForEventCache<String> waitForContextException(String id, Object clientKey) { + return mapCache(new IdEventKey<WaitForContextExceptionCache>(WaitForContextExceptionCache.class, id, clientKey) { + @Override + WaitForContextExceptionCache createCache() { + return new WaitForContextExceptionCache(); + } + }); + } + + public void contextException(String id, String msg) { + fStateResetMap.reset(id); + + // TODO: avoid iterating over all entries, use separate list for events. + for (Map.Entry<Key<?>, ICache<?>> entry: fMap.entrySet()) { + if (entry.getKey() instanceof IdEventKey) { + IdEventKey<?> eventKey = (IdEventKey<?>)entry.getKey(); + if ( WaitForContextExceptionCache.class.equals( eventKey.getCacheClass() ) && + eventKey.fId.equals(id) ) + { + ((WaitForContextExceptionCache)entry.getValue()).eventReceived(msg); + } + } + } + } + + private abstract class ContextEventKey<V> extends IdKey<V> { + private Object fClientKey; + + public ContextEventKey(Class<V> eventClazz, String id, Object clientKey) { + super(eventClazz, id); + fClientKey = clientKey; + } + + @Override + public boolean equals(Object obj) { + if (super.equals(obj) && obj instanceof ContextEventKey<?>) { + return ((ContextEventKey<?>)obj).fClientKey.equals(fClientKey); + } + return false; + } + + @Override + public int hashCode() { + return super.hashCode() + fClientKey.hashCode(); + } + } + + private class WaitForContextAddedCache extends WaitForEventCache<RunControlContext[]> {} + + public IWaitForEventCache<RunControlContext[]> waitForContextAdded(String parentId, Object clientKey) { + return mapCache(new ContextEventKey<WaitForContextAddedCache>(WaitForContextAddedCache.class, parentId, clientKey) { + @Override + WaitForContextAddedCache createCache() { + return new WaitForContextAddedCache(); + } + }); + } + + public void contextAdded(RunControlContext[] contexts) { + for (RunControlListener listener : fListeners) { + listener.contextAdded(contexts); + } + + for (RunControlContext context : contexts) { + fStateResetMap.reset(context.getID()); + } + + Set<String> parents = new TreeSet<String>(); + for (RunControlContext context : contexts) { + if (context.getParentID() != null) { + parents.add(context.getParentID()); + } + } + for (String parent : parents) { + fChildrenResetMap.reset(parent, false, false); + } + + for (Map.Entry<Key<?>, ICache<?>> entry: fMap.entrySet()) { + if (entry.getKey() instanceof ContextEventKey) { + ContextEventKey<?> eventKey = (ContextEventKey<?>)entry.getKey(); + if ( WaitForContextAddedCache.class.equals( eventKey.getCacheClass()) && + parents.contains(eventKey.getId()) ) + { + ((WaitForContextAddedCache)entry.getValue()).eventReceived(contexts); + } + } + } + } + + private class WaitForContextChangedCache extends WaitForEventCache<RunControlContext[]> {} + + public IWaitForEventCache<RunControlContext[]> waitForContextChanged(String id, Object clientKey) { + return mapCache(new ContextEventKey<WaitForContextChangedCache>(WaitForContextChangedCache.class, id, clientKey) { + @Override + WaitForContextChangedCache createCache() { + return new WaitForContextChangedCache(); + } + }); + } + + public void contextChanged(RunControlContext[] contexts) { + for (RunControlListener listener : fListeners) { + listener.contextChanged(contexts); + } + + for (RunControlContext context : contexts) { + fStateResetMap.reset(context.getID()); + fChildrenResetMap.reset(context.getID(), true, false); + } + + for (Map.Entry<Key<?>, ICache<?>> entry: fMap.entrySet()) { + if (entry.getKey() instanceof ContextEventKey) { + ContextEventKey<?> eventKey = (ContextEventKey<?>)entry.getKey(); + if ( WaitForContextChangedCache.class.equals( eventKey.getCacheClass()) && + contains(contexts, eventKey.getId()) ) + { + ((WaitForContextChangedCache)entry.getValue()).eventReceived(null); + } + } + } + } + + private class WaitForContextRemovedCache extends WaitForEventCache<String[]> {} + + public IWaitForEventCache<String[]> waitForContextRemoved(String id, Object clientKey) { + return mapCache(new IdEventKey<WaitForContextRemovedCache>(WaitForContextRemovedCache.class, id, clientKey) { + @Override + WaitForContextRemovedCache createCache() { + return new WaitForContextRemovedCache(); + } + }); + } + + public void contextRemoved(String[] context_ids) { + // Call client listeners first + for (RunControlListener listener : fListeners) { + listener.contextRemoved(context_ids); + } + + for (String context_id : context_ids) { + fChildrenResetMap.reset(context_id, false, true); + fStateResetMap.reset(context_id); + } + + for (Map.Entry<Key<?>, ICache<?>> entry: fMap.entrySet()) { + if (entry.getKey() instanceof IdEventKey) { + IdEventKey<?> eventKey = (IdEventKey<?>)entry.getKey(); + if ( WaitForContextRemovedCache.class.equals( eventKey.getCacheClass()) && + contains(context_ids, eventKey.getId()) ) + { + ((WaitForContextRemovedCache)entry.getValue()).eventReceived(context_ids); + } + } + } + } + +} diff --git a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/StackTraceCM.java b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/StackTraceCM.java new file mode 100644 index 000000000..5f8b582db --- /dev/null +++ b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/StackTraceCM.java @@ -0,0 +1,162 @@ +/******************************************************************************* + * Copyright (c) 2011 Wind River Systems 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.tcf.debug.test.services; + +import java.util.Map; + +import org.eclipse.tcf.debug.test.util.ICache; +import org.eclipse.tcf.debug.test.util.TokenCache; +import org.eclipse.tcf.protocol.IToken; +import org.eclipse.tcf.services.IRunControl; +import org.eclipse.tcf.services.IRunControl.RunControlContext; +import org.eclipse.tcf.services.IStackTrace; +import org.eclipse.tcf.services.IStackTrace.StackTraceContext; + +/** + * + */ +public class StackTraceCM extends AbstractCacheManager implements IRunControl.RunControlListener { + private IStackTrace fService; + private IRunControl fRunControl; + + public StackTraceCM(IStackTrace service, IRunControl runControl) { + fService = service; + fRunControl = runControl; + fRunControl.addListener(this); + } + + @Override + public void dispose() { + fRunControl.removeListener(this); + super.dispose(); + } + + private class ChildrenCache extends TokenCache<String[]> implements IStackTrace.DoneGetChildren { + private final String fId; + public ChildrenCache(String id) { + fId = id; + } + + @Override + protected IToken retrieveToken() { + return fService.getChildren(fId, this); + } + public void doneGetChildren(IToken token, Exception error, String[] context_ids) { + set(token, context_ids, error); + } + public void resetChildren() { + if (isValid()) reset(); + } + }; + + private class ChildrenCacheKey extends IdKey<ChildrenCache> { + public ChildrenCacheKey(String id) { + super(ChildrenCache.class, id); + } + @Override ChildrenCache createCache() { return new ChildrenCache(fId); } + } + + public ICache<String[]> getChildren(String id) { + return mapCache(new ChildrenCacheKey(id)); + } + + class ContextCache extends TokenCache<StackTraceContext> implements IStackTrace.DoneGetContext { + private final String fId; + + public ContextCache(String id) { + fId = id; + } + @Override + protected IToken retrieveToken() { + return fService.getContext(new String[] { fId }, this); + } + public void doneGetContext(IToken token, Exception error, StackTraceContext[] contexts) { + StackTraceContext context = contexts != null && contexts.length > 0 ? contexts[0] : null; + set(token, context, error); + } + public void resetContext() { + if (isValid()) reset(); + } + } + + private class ContextCacheKey extends IdKey<ContextCache> { + public ContextCacheKey(String id) { + super(ContextCache.class, id); + } + @Override ContextCache createCache() { return new ContextCache(fId); } + } + + public ICache<StackTraceContext>[] getContext(final String[] ids) { + @SuppressWarnings("unchecked") + ICache<StackTraceContext>[] caches = (ICache<StackTraceContext>[])new ICache[ids.length]; + for (int i = 0; i < ids.length; i++) { + caches[i] = mapCache(new ContextCacheKey(ids[i])); + } + return caches; + } + + public void contextAdded(RunControlContext[] contexts) { + // TODO Auto-generated method stub + + } + + public void contextChanged(RunControlContext[] contexts) { + for (RunControlContext context : contexts) { + resetRunControlContext(context.getID()); + } + } + + public void contextRemoved(String[] context_ids) { + for (String id : context_ids) { + resetRunControlContext(id); + } + } + + public void contextSuspended(String context, String pc, String reason, Map<String, Object> params) { + resetRunControlContext(context); + } + + public void contextResumed(String context) { + resetRunControlContext(context); + } + + public void containerSuspended(String context, String pc, String reason, Map<String, Object> params, + String[] suspended_ids) + { + for (String id : suspended_ids) { + resetRunControlContext(id); + } + } + + public void containerResumed(String[] context_ids) { + for (String id : context_ids) { + resetRunControlContext(id); + } + } + + public void contextException(String context, String msg) { + resetRunControlContext(context); + } + + private void resetRunControlContext(String id) { + ChildrenCache childrenCache = getCache(new ChildrenCacheKey(id)); + if (childrenCache != null && childrenCache.isValid() && childrenCache.getData() != null) { + String[] frameIds = childrenCache.getData(); + for (String frameId : frameIds) { + ContextCache contextCache = getCache(new ContextCacheKey(frameId)); + if (contextCache != null) { + contextCache.resetContext(); + } + } + childrenCache.resetChildren(); + } + } +} diff --git a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/SymbolsCM.java b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/SymbolsCM.java new file mode 100644 index 000000000..3a1ce110c --- /dev/null +++ b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/SymbolsCM.java @@ -0,0 +1,422 @@ +/******************************************************************************* + * Copyright (c) 2011 Wind River Systems 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.tcf.debug.test.services; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ExecutionException; + +import org.eclipse.tcf.debug.test.util.AbstractCache; +import org.eclipse.tcf.debug.test.util.CallbackCache; +import org.eclipse.tcf.debug.test.util.DataCallback; +import org.eclipse.tcf.debug.test.util.ICache; +import org.eclipse.tcf.debug.test.util.TokenCache; +import org.eclipse.tcf.debug.test.util.Transaction; +import org.eclipse.tcf.debug.test.util.Transaction.InvalidCacheException; +import org.eclipse.tcf.protocol.IToken; +import org.eclipse.tcf.services.IMemoryMap; +import org.eclipse.tcf.services.IMemoryMap.MemoryMapListener; +import org.eclipse.tcf.services.IRunControl.RunControlContext; +import org.eclipse.tcf.services.IRunControl.RunControlListener; +import org.eclipse.tcf.services.ISymbols; +import org.eclipse.tcf.services.ISymbols.Symbol; + +/** + * + */ +public class SymbolsCM extends AbstractCacheManager { + + private ResetMap fRunControlResetMap = new ResetMap(); + private ResetMap fMemoryResetMap = new ResetMap(); + + private ISymbols fService; + private IMemoryMap fMemoryMap; + private RunControlCM fRunControlCM; + + public SymbolsCM(ISymbols service, RunControlCM runControl, IMemoryMap memoryMap) { + fService = service; + fRunControlCM = runControl; + fRunControlCM.addListener(fRunControlListener); + fMemoryMap = memoryMap; + fMemoryMap.addListener(fMemoryListener); + } + + @Override + public void dispose() { + fRunControlCM.removeListener(fRunControlListener); + fMemoryMap.removeListener(fMemoryListener); + super.dispose(); + } + + private static final List<String> ANY_ID_PARENTS = new ArrayList<String>(1); + { + ANY_ID_PARENTS.add(ResetMap.ANY_ID); + } + + abstract private class SymbolCache<V> extends CallbackCache<V> { + protected final AbstractCache<V> fInner; + private Symbol fSymbol; + private List<String> fParents = new ArrayList<String>(4); + + public SymbolCache(AbstractCache<V> inner) { + fInner = inner; + } + + public void reset() { + super.reset(); + if (fInner.isValid()) fInner.reset(); + } + + abstract protected String getSymbolId(); + + @Override + protected void retrieve(final DataCallback<V> rm) { + fRunControlResetMap.addPending(this); + fMemoryResetMap.addPending(this); + Transaction<V> transaction = new Transaction<V>() { + protected V process() throws InvalidCacheException, ExecutionException { + V retVal = processInner(this); + fSymbol = processSymbol(this); + fParents = processParents(this); + return retVal; + } + }; + transaction.request(rm); + } + + protected V processInner(Transaction<V> t) throws InvalidCacheException, ExecutionException { + return t.validate(fInner); + } + + protected Symbol processSymbol(Transaction<V> t) throws InvalidCacheException, ExecutionException { + return t.validate( getContext(getSymbolId()) ); + } + + protected List<String> processParents(Transaction<V> t) throws InvalidCacheException, ExecutionException { + List<String> parents = new ArrayList<String>(2); + String rcContextId = fSymbol.getOwnerID(); + while( rcContextId != null ) { + parents.add(rcContextId); + RunControlContext rcContext = t.validate( fRunControlCM.getContext(rcContextId) ); + rcContextId = rcContext.getParentID(); + } + return parents; + } + + @Override + protected void handleCompleted(V data, Throwable error, boolean canceled) { + if (canceled) return; + + // If we cannot retrieve the symbol's context. Reset the cache on + // any rc event. + List<String> parents = ANY_ID_PARENTS; + int updatePolicy = ISymbols.UPDATE_ON_EXE_STATE_CHANGES; + if (error == null) { + parents = fParents; + updatePolicy = fSymbol.getUpdatePolicy(); + } + updateRunControlResetMap(parents, updatePolicy, data, error); + updateMemoryMapResetMap(parents, data, error); + } + + private void updateRunControlResetMap(List<String> parents, int updatePolicy, V data, Throwable error) { + Set<String> pendingIds = fRunControlResetMap.removePending(this); + if (updatePolicy == ISymbols.UPDATE_ON_EXE_STATE_CHANGES) { + String ownerId = parents.get(0); + if (pendingIds.contains(ownerId) || (ResetMap.ANY_ID.equals(ownerId) && !pendingIds.isEmpty())) { + // Reset cache immediately after setting value. + set(data, error, false); + } else { + fRunControlResetMap.addValid(ownerId, this); + set(data, error, true); + } + } + } + + private void updateMemoryMapResetMap(List<String> parents, V data, Throwable error) { + Set<String> pendingIds = fMemoryResetMap.removePending(this); + boolean resetPending = false; + if (!pendingIds.isEmpty()) { + if (ResetMap.ANY_ID.equals(parents.get(0))) { + resetPending = true; + } else { + for (String parent : parents) { + if (pendingIds.contains(parent)) { + resetPending = true; + } + } + } + } + + if (resetPending) { + // Reset cache immediately after setting value. + set(data, error, false); + } else { + fMemoryResetMap.addValid(parents, this); + set(data, error, true); + } + } + } + + private class ChildrenCache extends SymbolCache<String[]> { + public ChildrenCache(InnerChildrenCache inner) { + super(inner); + } + + @Override + protected String getSymbolId() { + return ((InnerChildrenCache)fInner).fId; + } + } + + private class InnerChildrenCache extends TokenCache<String[]> implements ISymbols.DoneGetChildren { + private final String fId; + public InnerChildrenCache(String id) { + fId = id; + } + + @Override + protected IToken retrieveToken() { + fRunControlResetMap.addPending(this); + return fService.getChildren(fId, this); + } + public void doneGetChildren(IToken token, Exception error, String[] context_ids) { + set(token, context_ids, error); + } + }; + + private class ChildrenCacheKey extends IdKey<ChildrenCache> { + public ChildrenCacheKey(String id) { + super(ChildrenCache.class, id); + } + @Override ChildrenCache createCache() { return new ChildrenCache( new InnerChildrenCache(fId) ); } + } + + public ICache<String[]> getChildren(String id) { + return mapCache(new ChildrenCacheKey(id)); + } + + private class ContextCache extends SymbolCache<Symbol> { + public ContextCache(InnerContextCache inner) { + super(inner); + } + @Override + protected String getSymbolId() { + return fInner.getData().getID(); + } + @Override + protected Symbol processSymbol(Transaction<Symbol> t) throws InvalidCacheException, ExecutionException { + return fInner.getData(); + } + } + + class InnerContextCache extends TokenCache<Symbol> implements ISymbols.DoneGetContext { + private final String fId; + + public InnerContextCache(String id) { + fId = id; + } + @Override + protected IToken retrieveToken() { + return fService.getContext(fId, this); + } + public void doneGetContext(IToken token, Exception error, Symbol symbol) { + set(token, symbol, error); + } + public void resetContext() { + if (isValid()) reset(); + } + } + + private class ContextCacheKey extends IdKey<ContextCache> { + public ContextCacheKey(String id) { + super(ContextCache.class, id); + } + @Override ContextCache createCache() { return new ContextCache( new InnerContextCache(fId)); } + } + + public ICache<Symbol> getContext(String id) { + return mapCache(new ContextCacheKey(id)); + } + + private class FindCache extends SymbolCache<String> { + public FindCache(InnerFindCache inner) { + super(inner); + } + @Override + protected String getSymbolId() { + return fInner.getData(); + } + } + + class InnerFindCache extends TokenCache<String> implements ISymbols.DoneFind { + private final String fId; + private final Number fIp; + private final String fName; + + public InnerFindCache(String id, Number ip, String name) { + fId = id; + fIp = ip; + fName = name; + } + @Override + protected IToken retrieveToken() { + return fService.find(fId, fIp, fName, this); + } + + public void doneFind(IToken token, Exception error, String symbol_id) { + set(token, symbol_id, error); + } + } + + private class FindCacheKey extends IdKey<FindCache> { + private final Number fIp; + private final String fName; + + public FindCacheKey(String id, Number ip, String name) { + super(FindCache.class, id); + fIp = ip; + fName = name; + } + @Override FindCache createCache() { return new FindCache(new InnerFindCache(fId, fIp, fName)); } + + @Override + public boolean equals(Object obj) { + if (super.equals(obj) && obj instanceof FindCacheKey) { + FindCacheKey other = (FindCacheKey)obj; + return fIp.equals(other.fIp) && fName.equals(other.fName); + } + return false; + } + @Override + public int hashCode() { + return super.hashCode() + fIp.hashCode() + fName.hashCode(); + } + } + + public ICache<String> find(String context_id, Number ip, String name) { + return mapCache(new FindCacheKey(context_id, ip, name)); + } + + private class FindByAddrCache extends SymbolCache<String> { + + public FindByAddrCache(InnerFindByAddrCache inner) { + super(inner); + } + + @Override + protected String getSymbolId() { + return fInner.getData(); + } + } + + private class InnerFindByAddrCache extends TokenCache<String> implements ISymbols.DoneFind { + private final String fId; + private final Number fAddr; + + public InnerFindByAddrCache(String id, Number addr) { + fId = id; + fAddr = addr; + } + @Override + protected IToken retrieveToken() { + return fService.findByAddr(fId, fAddr, this); + } + + public void doneFind(IToken token, Exception error, String symbol_id) { + set(token, symbol_id, error); + } + } + + private class FindByAddrCacheKey extends IdKey<FindByAddrCache> { + private final Number fAddr; + + public FindByAddrCacheKey(String id, Number addr) { + super(FindByAddrCache.class, id); + fAddr = addr; + } + @Override FindByAddrCache createCache() { return new FindByAddrCache(new InnerFindByAddrCache(fId, fAddr)); } + + @Override + public boolean equals(Object obj) { + if (super.equals(obj) && obj instanceof FindByAddrCacheKey) { + FindByAddrCacheKey other = (FindByAddrCacheKey)obj; + return fAddr.equals(other.fAddr); + } + return false; + } + @Override + public int hashCode() { + return super.hashCode() + fAddr.hashCode(); + } + } + + public ICache<String> findByAddr(String context_id, Number addr) { + return mapCache(new FindByAddrCacheKey(context_id, addr)); + } + + private RunControlListener fRunControlListener = new RunControlListener() { + + public void contextAdded(RunControlContext[] contexts) { + } + + public void contextChanged(RunControlContext[] contexts) { + for (RunControlContext context : contexts) { + resetRunControlContext(context.getID()); + } + } + + public void contextRemoved(String[] context_ids) { + for (String id : context_ids) { + resetRunControlContext(id); + fMemoryResetMap.reset(id); + } + } + + public void contextSuspended(String context, String pc, String reason, Map<String, Object> params) { + resetRunControlContext(context); + } + + public void contextResumed(String context) { + resetRunControlContext(context); + } + + public void containerSuspended(String context, String pc, String reason, Map<String, Object> params, + String[] suspended_ids) + { + for (String id : suspended_ids) { + resetRunControlContext(id); + } + } + + public void containerResumed(String[] context_ids) { + for (String id : context_ids) { + resetRunControlContext(id); + } + } + + public void contextException(String context, String msg) { + resetRunControlContext(context); + } + }; + + private void resetRunControlContext(String id) { + fRunControlResetMap.reset(id); + } + + private MemoryMapListener fMemoryListener = new MemoryMapListener() { + public void changed(String context_id) { + fMemoryResetMap.reset(context_id); + } + }; +} diff --git a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/WaitForEventCache.java b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/WaitForEventCache.java new file mode 100644 index 000000000..3966c58bf --- /dev/null +++ b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/WaitForEventCache.java @@ -0,0 +1,16 @@ +package org.eclipse.tcf.debug.test.services; + +import org.eclipse.tcf.debug.test.util.AbstractCache; + +class WaitForEventCache<V> extends AbstractCache<V> implements IWaitForEventCache<V> { + @Override + protected void retrieve() { } // no-op - called by listener + @Override + protected void canceled() { } // no-op - no command sent + + public void eventReceived(V data) { + if (!isValid()) { + set(data, null, true); // notify listeners + } + } +}
\ No newline at end of file diff --git a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/AbstractCache.java b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/AbstractCache.java new file mode 100644 index 000000000..29dbd81f1 --- /dev/null +++ b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/AbstractCache.java @@ -0,0 +1,314 @@ +package org.eclipse.tcf.debug.test.util; + +/******************************************************************************* + * Copyright (c) 2008 Wind River Systems, Inc. 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CancellationException; + +import org.eclipse.core.runtime.IStatus; +import org.eclipse.tcf.protocol.Protocol; + +/** + * A base implementation of a general purpose cache. Sub classes must implement + * {@link #retrieve(DataRequestMonitor)} to fetch data from the data source. + * Sub-classes are also responsible for calling {@link #set(Object, IStatus)} + * and {@link #reset()} to manage the state of the cache in response to events + * from the data source. + * <p> + * This cache requires an executor to use. The executor is used to synchronize + * access to the cache state and data. + * </p> + * @since 2.2 + */ +public abstract class AbstractCache<V> implements ICache<V> { + + private static final Throwable INVALID_STATUS = new Throwable("Cache invalid"); //$NON-NLS-1$ + + private class RequestCanceledListener implements Callback.ICanceledListener { + public void requestCanceled(final Callback canceledRm) { + invokeInDispatchThread(new Runnable() { + public void run() { + handleCanceledRm(canceledRm); + } + }); + } + }; + + private RequestCanceledListener fRequestCanceledListener = new RequestCanceledListener(); + + private boolean fValid; + + private V fData; + private Throwable fError = INVALID_STATUS; + + private Object fWaitingList; + + /** + * Sub-classes should override this method to retrieve the cache data from + * its source. The implementation should call {@link #set(Object, IStatus)} + * to store the newly retrieved data when it arrives (or an error, if one + * occurred retrieving the data) + * + * @param rm + * Request monitor for completion of data retrieval. + */ + abstract protected void retrieve(); + + protected void invokeInDispatchThread(Runnable runnable) { + if (Protocol.isDispatchThread()) { + runnable.run(); + } else { + Protocol.invokeLater(runnable); + } + } + + /** + * Called to cancel a retrieve request. This method is called when + * clients of the cache no longer need data that was requested. <br> + * Sub-classes should cancel and clean up requests to the asynchronous + * data source. + * + * <p> + * Note: Called while holding a lock to "this". No new request will start until + * this call returns. + * </p> + */ + abstract protected void canceled(); + + public boolean isValid() { + return fValid; + } + + public V getData() { + if (!fValid) { + throw new IllegalStateException("Cache is not valid. Cache data can be read only when cache is valid."); //$NON-NLS-1$ + } + return fData; + } + + public Throwable getError() { + if (!fValid) { + throw new IllegalStateException("Cache is not valid. Cache status can be read only when cache is valid."); //$NON-NLS-1$ + } + return fError; + } + + public void update(Callback rm) { + assert Protocol.isDispatchThread(); + + if (!fValid) { + boolean first = false; + synchronized (this) { + if (fWaitingList == null) { + first = true; + fWaitingList = rm; + } else if (fWaitingList instanceof Callback[]) { + Callback[] waitingList = (Callback[])fWaitingList; + int waitingListLength = waitingList.length; + int i; + for (i = 0; i < waitingListLength; i++) { + if (waitingList[i] == null) { + waitingList[i] = rm; + break; + } + } + if (i == waitingListLength) { + Callback[] newWaitingList = new Callback[waitingListLength + 1]; + System.arraycopy(waitingList, 0, newWaitingList, 0, waitingListLength); + newWaitingList[waitingListLength] = rm; + fWaitingList = newWaitingList; + } + } else { + Callback[] newWaitingList = new Callback[2]; + newWaitingList[0] = (Callback)fWaitingList; + newWaitingList[1] = rm; + fWaitingList = newWaitingList; + } + } + rm.addCancelListener(fRequestCanceledListener); + if (first) { + retrieve(); + } + } else { + rm.setError(fError); + rm.done(); + } + } + + private void completeWaitingRms() { + Object waiting = null; + synchronized(this) { + waiting = fWaitingList; + fWaitingList = null; + } + if (waiting != null) { + if (waiting instanceof Callback) { + completeWaitingRm((Callback)waiting); + } else if (waiting instanceof Callback[]) { + Callback[] waitingList = (Callback[])waiting; + for (int i = 0; i < waitingList.length; i++) { + if (waitingList[i] != null) { + completeWaitingRm(waitingList[i]); + } + } + } + waiting = null; + } + } + + private void completeWaitingRm(Callback rm) { + rm.setError(fError); + rm.removeCancelListener(fRequestCanceledListener); + rm.done(); + } + + private void handleCanceledRm(final Callback rm) { + + boolean found = false; + boolean waiting = false; + synchronized (this) { + if (rm.equals(fWaitingList)) { + found = true; + waiting = false; + fWaitingList = null; + } else if(fWaitingList instanceof Callback[]) { + Callback[] waitingList = (Callback[])fWaitingList; + for (int i = 0; i < waitingList.length; i++) { + if (!found && rm.equals(waitingList[i])) { + waitingList[i] = null; + found = true; + } + waiting = waiting || waitingList[i] != null; + } + } + if (found && !waiting) { + canceled(); + } + } + + // If we have no clients waiting anymore, cancel the request + if (found) { + // We no longer need to listen to cancellations. + rm.removeCancelListener(fRequestCanceledListener); + rm.setError(new CancellationException()); + rm.done(); + } + + } + + /** + * Returns true if there are no clients waiting for this cache or if the + * clients that are waiting, have already canceled their requests. + * <p> + * Note: Calling this method may cause the client request monitors that were + * canceled to be completed with a cancel status. If all the client request + * monitors were canceled, this method will also cause the {@link #canceled()} + * method to be called. Both of these side effects will only happen + * asynchronously after <code>isCanceled()</code> returns. + * </p> + * + * @return <code>true</code> if all clients waiting on this cache have been + * canceled, or if there are no clients waiting at all. + */ + protected boolean isCanceled() { + boolean canceled; + List<Callback> canceledRms = null; + synchronized (this) { + if (fWaitingList instanceof Callback) { + if ( ((Callback)fWaitingList).isCanceled() ) { + canceledRms = new ArrayList<Callback>(1); + canceledRms.add((Callback)fWaitingList); + canceled = true; + } else { + canceled = false; + } + } else if(fWaitingList instanceof Callback[]) { + canceled = true; + Callback[] waitingList = (Callback[])fWaitingList; + for (int i = 0; i < waitingList.length; i++) { + if (waitingList[i] != null) { + if (waitingList[i].isCanceled()) { + if (canceledRms == null) { + canceledRms = new ArrayList<Callback>(1); + } + canceledRms.add( waitingList[i] ); + } else { + canceled = false; + } + } + } + } else { + assert fWaitingList == null; + canceled = true; + } + } + if (canceledRms != null) { + final List<Callback> _canceledRms = canceledRms; + Protocol.invokeLater(new Runnable() { + public void run() { + for (Callback canceledRm : _canceledRms) { + handleCanceledRm(canceledRm); + } + } + }); + } + + return canceled; + } + + /** + * Resets the cache, setting the data to null and the status to + * INVALID_STATUS. When in the invalid state, neither the data nor the + * status can be queried. + */ + public void reset() { + set(null, INVALID_STATUS, false); + } + + /** + * Sets data and error values into the cache, and optionally puts in valid + * state. Note that data may be null and status may be an error status. + * 'Valid' simply means that our data is not stale. In other words, if the + * request to the source encounters an error, the cache object becomes valid + * all the same. The status indicates what error was encountered. + * + * <p> + * This method is called internally, typically in response to having + * obtained the result from the asynchronous request to the source. The + * data/status will remain valid until the cache object receives an event + * notification from the source indicating otherwise. + * + * @param data + * The data that should be returned to any clients waiting for + * cache data and for clients requesting data until the cache is + * invalidated. + * @param error The status that should be returned to any clients waiting for + * cache data and for clients requesting data until the cache is + * invalidated + * @param valid Whether the cache should bet set in valid state. If false, + * any callback waiting for data are completed but the cache + * is moved back to invalid state. + * + * @see #reset + */ + public void set(V data, Throwable error, boolean valid) { + assert Protocol.isDispatchThread(); + + fData = data; + fError = error; + fValid = valid; + + completeWaitingRms(); + } + +} diff --git a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/AggregateCallback.java b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/AggregateCallback.java index 1e3281d1a..25313f12f 100644 --- a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/AggregateCallback.java +++ b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/AggregateCallback.java @@ -100,7 +100,7 @@ public class AggregateCallback extends Callback { @Override public synchronized void setError(Throwable error) { if ((getError() == null)) { - setError(new AggregateError("") { + super.setError(new AggregateError("") { private static final long serialVersionUID = 1L; @Override diff --git a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/Callback.java b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/Callback.java index 78c7e47bc..227112fbe 100644 --- a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/Callback.java +++ b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/Callback.java @@ -251,6 +251,11 @@ public class Callback { } } + public void done(Throwable error) { + setError(error); + done(); + } + /** * Marks this request as completed. Once this method is called, the * monitor submits a runnable to the DSF Executor to call the @@ -261,6 +266,8 @@ public class Callback { * </p> */ public synchronized void done() { + assert Protocol.getEventQueue().isDispatchThread(); + if (fDone) { throw new IllegalStateException("Callback: " + this + ", done() method called more than once"); //$NON-NLS-1$//$NON-NLS-2$ } @@ -276,19 +283,7 @@ public class Callback { fParentCallback.removeCancelListener(fCanceledListener); } - try { - Protocol.invokeLater(new Runnable() { - public void run() { - Callback.this.handleCompleted(); - } - @Override - public String toString() { - return "Completed: " + Callback.this.toString(); //$NON-NLS-1$ - } - }); - } catch (IllegalStateException e) { - handleEventQueueShutDown(e); - } + handleCompleted(); } @Override @@ -404,21 +399,4 @@ public class Callback { fParentCallback.done(); } } - - /** - * Default handler for when the executor supplied in the constructor - * rejects the runnable that is submitted invoke this request monitor. - * This usually happens only when the executor is shutting down. - * <p> - * The default handler creates a new error status for the rejected - * execution and propagates it to the client or logs it. - */ - protected void handleEventQueueShutDown(IllegalStateException e) { - if (fParentCallback != null) { - fParentCallback.setError(e); - fParentCallback.done(); - } else { - Protocol.log("In callback " + toString() + ", unhandled event queue shut down.", e); - } - } } diff --git a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/CallbackCache.java b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/CallbackCache.java new file mode 100644 index 000000000..9275118c7 --- /dev/null +++ b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/CallbackCache.java @@ -0,0 +1,100 @@ +package org.eclipse.tcf.debug.test.util; + +/******************************************************************************* + * Copyright (c) 2008 Wind River Systems, Inc. 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ + + +/** + * A general purpose cache, which caches the result of a single request. + * Sub classes need to implement {@link #retrieve(DataRequestMonitor)} to fetch + * data from the data source. Clients are responsible for calling + * {@link #disable()} and {@link #reset()} to manage the state of the cache in + * response to events from the data source. + * <p> + * This cache requires an executor to use. The executor is used to synchronize + * access to the cache state and data. + * </p> + * @since 2.2 + */ +public abstract class CallbackCache<V> extends AbstractCache<V> { + + protected DataCallback<V> fRm; + + @Override + protected final void retrieve() { + // Make sure to cancel the previous rm. This may lead to the rm being + // canceled twice, but that's not harmful. + if (fRm != null) { + fRm.cancel(); + } + + fRm = new DataCallback<V>(null) { + @Override + protected void handleCompleted() { + if (this == fRm) { + fRm = null; + CallbackCache.this.handleCompleted(getData(), getError(), isCanceled()); + } + } + + @Override + public boolean isCanceled() { + return super.isCanceled() || CallbackCache.this.isCanceled(); + }; + }; + retrieve(fRm); + } + + /** + * Sub-classes should override this method to retrieve the cache data + * from its source. + * + * @param rm Request monitor for completion of data retrieval. + */ + protected abstract void retrieve(DataCallback<V> rm); + + protected void handleCompleted(V data, Throwable error, boolean canceled) { + // If the requestor canceled the request, then leave the + // cache as is, regardless of how the retrieval completes. + // We want the cache to stay in the invalid state so that + // it remains functional. The request may have completed + // successfully, but using it may produce inconsistent + // results. + if (!canceled) { + set(data, error, true); + } + } + + @Override + protected synchronized void canceled() { + if (fRm != null) { + fRm.cancel(); + } + } + + @Override + public void set(V data, Throwable error, boolean valid) { + if (fRm != null) { + fRm.cancel(); + fRm = null; + } + super.set(data, error, valid); + } + + @Override + public void reset() { + if (fRm != null) { + fRm.cancel(); + fRm = null; + } + super.reset(); + } +} diff --git a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/DataCallback.java b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/DataCallback.java index e9822fa16..8127d9e06 100644 --- a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/DataCallback.java +++ b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/DataCallback.java @@ -46,6 +46,11 @@ public class DataCallback<V> extends Callback { */ public synchronized V getData() { return fData; } + public void done(V data, Throwable error) { + setData(data); + done(error); + } + @Override public String toString() { if (getData() != null) { diff --git a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/ICache.java b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/ICache.java new file mode 100644 index 000000000..eca1bb713 --- /dev/null +++ b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/ICache.java @@ -0,0 +1,58 @@ +/******************************************************************************* + * Copyright (c) 2010 Wind River Systems 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.tcf.debug.test.util; + + +/** + * The interface for a general purpose cache that caches the result of a single + * request. Implementations need to provide the logic to fetch data from an + * asynchronous data source. + * <p> + * This cache requires an executor to use. The executor is used to synchronize + * access to the cache state and data. + * </p> + * @since 2.2 + */ +public interface ICache<V> { + + /** + * Returns the current data value held by this cache. Clients should first + * call isValid() to determine if the data is up to date. Calling this when + * in the invalid state will throw an IllegalStateException + */ + public V getData(); + + /** + * Returns the status of the source request held by this cache. Clients + * should first call isValid() to determine if the data is up to date. + * Calling this when in the invalid state will throw an + * IllegalStateException + */ + public Throwable getError(); + + /** + * Asks the cache to update its value from the source. If the cache is + * already valid, the request is completed immediately, otherwise data will + * first be retrieved from the source. Typically, this method is called by a + * client after it discovers the cache is invalid via {@link #isValid()} + * + * @param rm + * RequestMonitor that is called when cache becomes valid. + */ + public void update(Callback rm); + + /** + * Returns <code>true</code> if the cache is currently valid. I.e. + * whether the cache can return a value immediately without first + * retrieving it from the data source. + */ + public boolean isValid(); +} diff --git a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/TokenCache.java b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/TokenCache.java new file mode 100644 index 000000000..e19b73457 --- /dev/null +++ b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/TokenCache.java @@ -0,0 +1,46 @@ +/******************************************************************************* + * Copyright (c) 2011 Wind River Systems 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.tcf.debug.test.util; + +import java.util.concurrent.atomic.AtomicReference; + +import org.eclipse.tcf.protocol.IToken; + +/** + * + */ +public abstract class TokenCache<V> extends AbstractCache<V> { + + private AtomicReference<IToken> fToken = new AtomicReference<IToken>(); + + @Override + final protected void retrieve() { + fToken.set(retrieveToken()); + } + + protected boolean checkToken(IToken token) { + return fToken.compareAndSet(token, null); + } + + abstract protected IToken retrieveToken(); + + protected void set(IToken token, V data, Throwable error) { + if (checkToken(token) ) { + set(data, error, true); + } + } + + @Override + protected void canceled() { + IToken token = fToken.getAndSet(null); + token.cancel(); + } +} diff --git a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/Transaction.java b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/Transaction.java new file mode 100644 index 000000000..d27281eaf --- /dev/null +++ b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/Transaction.java @@ -0,0 +1,254 @@ +/******************************************************************************* + * Copyright (c) 2008 Wind River Systems 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.tcf.debug.test.util; + +import java.util.Arrays; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import org.eclipse.core.runtime.CoreException; + +/** + * @since 2.2 + */ +public abstract class Transaction<V> implements Future<V> { + + /** + * The exception we throw when the client transaction logic asks us to + * validate a cache object that is stale (or has never obtained a value from + * the source) + */ + private static final InvalidCacheException INVALID_CACHE_EXCEPTION = new InvalidCacheException(); + + /** The request object we've been given to set the transaction results in */ + private DataCallback<V> fRm; + + private Query<V> fQuery; + + public static class InvalidCacheException extends Exception { + private static final long serialVersionUID = 1L; + } + + /** + * Kicks off the transaction. We'll either complete the request monitor + * immediately if all the data points the transaction needs are cached and + * valid, or we'll end up asynchronously completing the monitor if and when + * either (a) all the data points are available and up-to-date, or (b) + * obtaining them from the source encountered an error. Note that there is + * potential in (b) for us to never complete the monitor. If one or more + * data points are perpetually becoming stale, then we'll indefinitely wait + * for them to stabilize. The caller should cancel its request monitor in + * order to get us to stop waiting. + * + * @param rm Request completion monitor. + */ + public void request(DataCallback<V> rm) { + if (fRm != null) { + assert fRm.isCanceled(); + fRm.done(); + } + fRm = rm; + assert fRm != null; + execute(); + } + + /** + * The transaction logic--code that tries to synchronously make use of, + * usually, multiple data points that are normally obtained asynchronously. + * Each data point is represented by a cache object. The transaction logic + * must check the validity of each cache object just prior to using it + * (calling its getData()). It should do that check by calling one of our + * validate() methods. Those methods will throw InvalidCacheException if the + * cached data is invalid (stale, e.g.,) or CoreException if an error was + * encountered the last time it got data form the source. The exception will + * abort the transaction, but in the case of InvalidCacheException, we + * schedule an asynchronous call that will re-invoke the transaction + * logic once the cache object has been updated from the source. + * + * @return the cached data if it's valid, otherwise an exception is thrown + * @throws Transaction.InvalidCacheException Exception indicating that a + * cache is not valid and transaction will need to be rescheduled. + * @throws CoreException Exception indicating that one of the caches is + * in error state and transaction cannot be processed. + */ + abstract protected V process() throws InvalidCacheException, ExecutionException; + + /** + * Method which invokes the transaction logic and handles any exception that + * may result. If that logic encounters a stale/unset cache object, then we + * simply do nothing. This method will be called again once the cache + * objects tell us it has obtained an updated value form the source. + */ + private void execute() { + if (fRm.isCanceled()) { + fRm.done(); + fRm = null; + return; + } + + try { + // Execute the transaction logic + V data = process(); + + // No exception means all cache objects used by the transaction + // were valid and up to date. Complete the request + fRm.setData(data); + fRm.done(); + fRm = null; + } + catch (InvalidCacheException e) { + // At least one of the cache objects was stale/unset. Keep the + // request monitor in the incomplete state, thus leaving our client + // "waiting" (asynchronously). We'll get called again once the cache + // objects are updated, thus re-starting the whole transaction + // attempt. + } + catch (Throwable e) { + // At least one of the cache objects encountered a failure obtaining + // the data from the source. Complete the request. + fRm.setError(e); + fRm.done(); + fRm = null; + } + + } + + /** + * Clients must call one of our validate methods prior to using (calling + * getData()) on data cache object. + * + * @param cache + * the object being validated + * @throws InvalidCacheException + * if the data is stale/unset + * @throws ExecutionException + * if an error was encountered getting the data from the source + */ + public <T> T validate(ICache<T> cache) throws InvalidCacheException, ExecutionException { + if (cache.isValid()) { + if (cache.getError() != null) { + throw new ExecutionException(cache.getError()); + } + return cache.getData(); + } else { + // Throw the invalid cache exception, but first ask the cache to + // update itself from its source, and schedule a re-attempt of the + // transaction logic to occur when the stale/unset cache has been + // updated + cache.update(new Callback(fRm) { + @Override + protected void handleCompleted() { + execute(); + } + }); + throw INVALID_CACHE_EXCEPTION; + } + } + + /** + * See {@link #validate(RequestCache)}. This variant simply validates + * multiple cache objects. + */ + public <T> void validate(ICache<?> ... caches) throws InvalidCacheException, ExecutionException { + validate(Arrays.asList(caches)); + } + + /** + * See {@link #validate(RequestCache)}. This variant simply validates + * multiple cache objects. + */ + public void validate(@SuppressWarnings("rawtypes") Iterable caches) throws InvalidCacheException, ExecutionException { + // Check if any of the caches have errors: + boolean allValid = true; + + for (Object cacheObj : caches) { + ICache<?> cache = (ICache<?>)cacheObj; + if (cache.isValid()) { + if (cache.getError() != null) { + throw new ExecutionException(cache.getError()); + } + } else { + allValid = false; + } + } + if (!allValid) { + // Throw the invalid cache exception, but first schedule a + // re-attempt of the transaction logic, to occur when the + // stale/unset cache objects have been updated + AggregateCallback countringRm = new AggregateCallback(fRm) { + @Override + protected void handleCompleted() { + execute(); + } + }; + int count = 0; + for (Object cacheObj : caches) { + ICache<?> cache = (ICache<?>)cacheObj; + if (!cache.isValid()) { + cache.update(countringRm); + count++; + } + } + countringRm.setDoneCount(count); + throw INVALID_CACHE_EXCEPTION; + } + } + + private synchronized Query<V> getQuery(boolean create) { + if (fQuery == null && create) { + fQuery = new Query<V>() { + @Override + protected void execute(DataCallback<V> callback) { + request(callback); + } + }; + fQuery.invoke(); + } + + return fQuery; + } + + public boolean cancel(boolean mayInterruptIfRunning) { + Query<V> query = getQuery(false); + if (query != null) { + return query.cancel(mayInterruptIfRunning); + } + return false; + } + + public boolean isCancelled() { + Query<V> query = getQuery(false); + if (query != null) { + return query.isCancelled(); + } + return false; + } + + public boolean isDone() { + Query<V> query = getQuery(false); + if (query != null) { + return query.isDone(); + } + return false; + } + + public V get() throws InterruptedException, ExecutionException { + return getQuery(true).get(); + } + + public V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { + return getQuery(true).get(timeout, unit); + } + + +} |