summaryrefslogtreecommitdiffstatsabout
diff options
context:
space:
mode:
authorPawel Piech2012-01-26 00:21:48 (EST)
committer Eugene Tarassov2012-01-26 17:23:40 (EST)
commitcba02974993d9c14f544662c53db95c507ff60ee (patch)
treec322afc29792b975912df54d06aec3220c3fceea
parent3862e13b6e4a62adf3865bc5b2349cd146ea2e9d (diff)
downloadorg.eclipse.tcf-cba02974993d9c14f544662c53db95c507ff60ee.zip
org.eclipse.tcf-cba02974993d9c14f544662c53db95c507ff60ee.tar.gz
org.eclipse.tcf-cba02974993d9c14f544662c53db95c507ff60ee.tar.bz2
Bug 349998 - [test] JUnit tests for debug UI (Added initial breakpoints tests)
-rw-r--r--tests/plugins/org.eclipse.tcf.debug.test/.settings/org.eclipse.jdt.core.prefs87
-rw-r--r--tests/plugins/org.eclipse.tcf.debug.test/META-INF/MANIFEST.MF5
-rw-r--r--tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/AbstractTcfUITest.java223
-rw-r--r--tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/BreakpointsListener.java111
-rw-r--r--tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/BreakpointsTest.java166
-rw-r--r--tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/CacheTests.java832
-rw-r--r--tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/QueryTests.java255
-rw-r--r--tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/SampleTest.java292
-rw-r--r--tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/TransactionTests.java167
-rw-r--r--tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/ViewerUpdatesListener.java2
-rw-r--r--tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/BreakpointsCM.java18
-rw-r--r--tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/LineNumbersCM.java232
-rw-r--r--tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/AbstractCache.java70
-rw-r--r--tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/ICache.java18
-rw-r--r--tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/Transaction.java78
-rw-r--r--tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/TransactionCache.java111
16 files changed, 2287 insertions, 380 deletions
diff --git a/tests/plugins/org.eclipse.tcf.debug.test/.settings/org.eclipse.jdt.core.prefs b/tests/plugins/org.eclipse.tcf.debug.test/.settings/org.eclipse.jdt.core.prefs
index 9d7f7d2..3b0982c 100644
--- a/tests/plugins/org.eclipse.tcf.debug.test/.settings/org.eclipse.jdt.core.prefs
+++ b/tests/plugins/org.eclipse.tcf.debug.test/.settings/org.eclipse.jdt.core.prefs
@@ -1,8 +1,93 @@
-#Thu May 26 13:10:48 PDT 2011
eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.annotation.nonnull=org.eclipse.jdt.annotation.NonNull
+org.eclipse.jdt.core.compiler.annotation.nonnullbydefault=org.eclipse.jdt.annotation.NonNullByDefault
+org.eclipse.jdt.core.compiler.annotation.nonnullisdefault=disabled
+org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jdt.annotation.Nullable
+org.eclipse.jdt.core.compiler.annotation.nullanalysis=disabled
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
org.eclipse.jdt.core.compiler.compliance=1.5
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.autoboxing=ignore
+org.eclipse.jdt.core.compiler.problem.comparingIdentical=warning
+org.eclipse.jdt.core.compiler.problem.deadCode=warning
+org.eclipse.jdt.core.compiler.problem.deprecation=warning
+org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled
+org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled
+org.eclipse.jdt.core.compiler.problem.discouragedReference=warning
+org.eclipse.jdt.core.compiler.problem.emptyStatement=ignore
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=ignore
+org.eclipse.jdt.core.compiler.problem.fallthroughCase=ignore
+org.eclipse.jdt.core.compiler.problem.fatalOptionalError=enabled
+org.eclipse.jdt.core.compiler.problem.fieldHiding=ignore
+org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning
+org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning
+org.eclipse.jdt.core.compiler.problem.forbiddenReference=error
+org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning
+org.eclipse.jdt.core.compiler.problem.includeNullInfoFromAsserts=disabled
+org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning
+org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=ignore
+org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=ignore
+org.eclipse.jdt.core.compiler.problem.localVariableHiding=ignore
+org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning
+org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=ignore
+org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=ignore
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=ignore
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotationForInterfaceMethodImplementation=enabled
+org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning
+org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignore
+org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning
+org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning
+org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore
+org.eclipse.jdt.core.compiler.problem.nullReference=ignore
+org.eclipse.jdt.core.compiler.problem.nullSpecInsufficientInfo=warning
+org.eclipse.jdt.core.compiler.problem.nullSpecViolation=error
+org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning
+org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore
+org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=ignore
+org.eclipse.jdt.core.compiler.problem.potentialNullReference=ignore
+org.eclipse.jdt.core.compiler.problem.potentialNullSpecViolation=error
+org.eclipse.jdt.core.compiler.problem.potentiallyUnclosedCloseable=ignore
+org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning
+org.eclipse.jdt.core.compiler.problem.redundantNullAnnotation=warning
+org.eclipse.jdt.core.compiler.problem.redundantNullCheck=ignore
+org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=ignore
+org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=ignore
+org.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic=ignore
+org.eclipse.jdt.core.compiler.problem.reportMethodCanBeStatic=ignore
+org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled
+org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning
+org.eclipse.jdt.core.compiler.problem.suppressOptionalErrors=disabled
+org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled
+org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore
+org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning
+org.eclipse.jdt.core.compiler.problem.unavoidableGenericTypeProblems=enabled
+org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning
+org.eclipse.jdt.core.compiler.problem.unclosedCloseable=ignore
+org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore
+org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore
+org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=ignore
+org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=ignore
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.unusedImport=warning
+org.eclipse.jdt.core.compiler.problem.unusedLabel=warning
+org.eclipse.jdt.core.compiler.problem.unusedLocal=warning
+org.eclipse.jdt.core.compiler.problem.unusedObjectAllocation=ignore
+org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore
+org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled
+org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning
+org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning
org.eclipse.jdt.core.compiler.source=1.5
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 bbb6687..8b7ff52 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,8 +10,9 @@ 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.cdt.debug.core;bundle-version="7.2.0"
+ org.eclipse.debug.ui,
+ org.eclipse.cdt.debug.core;bundle-version="7.2.0",
+ org.eclipse.tcf.cdt.ui;bundle-version="1.0.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 5876824..46405ec 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
@@ -1,11 +1,14 @@
package org.eclipse.tcf.debug.test;
import java.io.IOException;
+import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.TreeMap;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -13,24 +16,32 @@ import java.util.regex.Pattern;
import junit.framework.TestCase;
+import org.eclipse.core.resources.IWorkspaceRunnable;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.debug.core.DebugException;
import org.eclipse.debug.core.DebugPlugin;
+import org.eclipse.debug.core.IBreakpointManager;
import org.eclipse.debug.core.ILaunch;
import org.eclipse.debug.core.ILaunchConfigurationType;
import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
import org.eclipse.debug.core.ILaunchManager;
import org.eclipse.debug.core.ILaunchesListener2;
+import org.eclipse.debug.core.model.IBreakpoint;
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.jface.viewers.TreePath;
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.LineNumbersCM;
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;
@@ -43,12 +54,15 @@ 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.Transaction;
+import org.eclipse.tcf.debug.ui.ITCFObject;
import org.eclipse.tcf.protocol.IChannel;
import org.eclipse.tcf.protocol.IPeer;
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.ILineNumbers;
import org.eclipse.tcf.services.IMemoryMap;
import org.eclipse.tcf.services.IRunControl;
import org.eclipse.tcf.services.IRunControl.RunControlContext;
@@ -89,13 +103,21 @@ public abstract class AbstractTcfUITest extends TestCase implements IViewerUpdat
protected IRunControl rc;
protected IBreakpoints bp;
protected IMemoryMap fMemoryMap;
+ protected ILineNumbers fLineNumbers;
protected RunControlCM fRunControlCM;
protected DiagnosticsCM fDiagnosticsCM;
protected BreakpointsCM fBreakpointsCM;
protected StackTraceCM fStackTraceCM;
protected SymbolsCM fSymbolsCM;
+ protected LineNumbersCM fLineNumbersCM;
+ protected String fTestId;
+ protected RunControlContext fTestCtx;
+ protected String fProcessId = "";
+ protected String fThreadId = "";
+ protected RunControlContext fThreadCtx;
+
private static class RemotePeer extends TransientPeer {
private final ArrayList<Map<String,String>> attrs;
@@ -137,7 +159,6 @@ public abstract class AbstractTcfUITest extends TestCase implements IViewerUpdat
}
protected void setUp() throws Exception {
-
fTestRunKey = new Object();
createDebugViewViewer();
@@ -194,6 +215,7 @@ public abstract class AbstractTcfUITest extends TestCase implements IViewerUpdat
}.get();
validateTestAvailable();
+ clearBreakpoints();
}
@Override
@@ -227,6 +249,7 @@ public abstract class AbstractTcfUITest extends TestCase implements IViewerUpdat
fBreakpointsCM = new BreakpointsCM(bp);
fStackTraceCM = new StackTraceCM(stk, rc);
fSymbolsCM = new SymbolsCM(syms, fRunControlCM, fMemoryMap);
+ fLineNumbersCM = new LineNumbersCM(fLineNumbers, fMemoryMap, fRunControlCM);
}
protected void tearDownServiceListeners() throws Exception{
@@ -235,6 +258,7 @@ public abstract class AbstractTcfUITest extends TestCase implements IViewerUpdat
fStackTraceCM.dispose();
fRunControlCM.dispose();
fDiagnosticsCM.dispose();
+ fLineNumbersCM.dispose();
}
private void createDebugViewViewer() {
@@ -278,11 +302,23 @@ public abstract class AbstractTcfUITest extends TestCase implements IViewerUpdat
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()) );
+ // The launch element may or may not have been populated into the viewer. It's label
+ // also may or may not have been updated. Check viewer item for launch, then wait if needed.
+ TreePath launchPath = new TreePath(new Object[] { fLaunch });
+ fDebugViewListener.addLabelUpdate(launchPath);
+ VirtualItem launchItem = fDebugViewViewer.findItem(launchPath);
+ fDebugViewListener.findElement(new Pattern[] { Pattern.compile(".*" + fLaunch.getLaunchConfiguration().getName() + ".*") } );
+ if (launchItem == null || launchItem.getData() == null || launchItem.getData(VirtualItem.LABEL_KEY) == null) {
+ fDebugViewListener.waitTillFinished(MODEL_CHANGED_COMPLETE | MODEL_PROXIES_INSTALLED | CONTENT_SEQUENCE_COMPLETE | LABEL_UPDATES);
+ launchItem = fDebugViewViewer.findItem(launchPath);
+ }
+
+ Assert.assertTrue( launchItem != null );
+ String[] launchItemLabel = (String[])launchItem.getData(VirtualItem.LABEL_KEY);
+ Assert.assertTrue( launchItemLabel[0].contains(fLaunch.getLaunchConfiguration().getName()) );
+ Assert.assertEquals(fLaunch, launchItem.getData());
}
private void terminateLaunch() throws DebugException, InterruptedException, ExecutionException {
@@ -328,6 +364,7 @@ public abstract class AbstractTcfUITest extends TestCase implements IViewerUpdat
rc = channels[0].getRemoteService(IRunControl.class);
bp = channels[0].getRemoteService(IBreakpoints.class);
fMemoryMap = channels[0].getRemoteService(IMemoryMap.class);
+ fLineNumbers = channels[0].getRemoteService(ILineNumbers.class);
};
});
}
@@ -473,6 +510,120 @@ public abstract class AbstractTcfUITest extends TestCase implements IViewerUpdat
Assert.assertTrue("Required test not supported", i != testList.length);
}
+ protected void clearBreakpoints() throws InterruptedException, ExecutionException, CoreException {
+
+ // Delete eclipse breakpoints.
+ IWorkspaceRunnable wr = new IWorkspaceRunnable() {
+ public void run(IProgressMonitor monitor) throws CoreException {
+ IBreakpointManager mgr = DebugPlugin.getDefault().getBreakpointManager();
+ IBreakpoint[] bps = DebugPlugin.getDefault().getBreakpointManager().getBreakpoints();
+ mgr.removeBreakpoints(bps, true);
+ }
+ };
+ ResourcesPlugin.getWorkspace().run(wr, null, 0, null);
+
+ // Delete TCF breakpoints
+ 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();
+ }
+
+ 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();
+ }
+
+ protected void initProcessModel(String testFunc) throws Exception {
+ String bpId = "entryPointBreakpoint";
+ createBreakpoint(bpId, testFunc);
+ fDebugViewListener.reset();
+
+ 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);
+ removeBreakpoint(bpId);
+
+ 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);
+ }
+
protected ContextState resumeAndWaitForSuspend(final RunControlContext context, final int mode) throws InterruptedException, ExecutionException {
return new Transaction<ContextState>() {
@Override
@@ -484,5 +635,67 @@ public abstract class AbstractTcfUITest extends TestCase implements IViewerUpdat
}
}.get();
}
-
+
+ protected 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 {
+ // Prime event wait caches
+ fBreakpointsCM.waitContextAdded(this);
+
+ validate( fBreakpointsCM.add(fBp, this) );
+ validate( fBreakpointsCM.waitContextAdded(this));
+
+ // 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 (fProcessId.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();
+ }
+
+ protected void removeBreakpoint(final String bpId) throws InterruptedException, ExecutionException {
+ new Transaction<Object>() {
+
+ @Override
+ protected Object process() throws InvalidCacheException, ExecutionException {
+
+ // Prime event wait caches
+ fBreakpointsCM.waitContextRemoved(this);
+
+ // Remove
+ validate( fBreakpointsCM.remove(new String[] { bpId }, this) );
+
+ // Verify removed event
+ String[] removedIds = validate( fBreakpointsCM.waitContextRemoved(this));
+ Assert.assertTrue(Arrays.asList(removedIds).contains(bpId));
+ return null;
+ }
+ }.get();
+ }
+
}
diff --git a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/BreakpointsListener.java b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/BreakpointsListener.java
new file mode 100644
index 0000000..3d432f2
--- /dev/null
+++ b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/BreakpointsListener.java
@@ -0,0 +1,111 @@
+/*******************************************************************************
+ * 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;
+
+import java.util.Collections;
+import java.util.Map;
+
+import org.eclipse.core.resources.IMarkerDelta;
+import org.eclipse.debug.core.DebugPlugin;
+import org.eclipse.debug.core.IBreakpointsListener;
+import org.eclipse.debug.core.model.IBreakpoint;
+
+/**
+ *
+ */
+public class BreakpointsListener implements IBreakpointsListener {
+
+ public enum EventType { ADDED, REMOVED, CHANGED };
+
+ public interface EventTester {
+ boolean checkEvent(EventType type, IBreakpoint bp, Map<String, Object> deltaAttributes);
+ }
+
+ private static final EventTester ANY_EVENT_TESTER = new EventTester() {
+ public boolean checkEvent(EventType type, IBreakpoint bp, Map<String,Object> deltaAttributes) {
+ return true;
+ }
+ };
+
+ private EventTester fTester = ANY_EVENT_TESTER;
+ private boolean fWaiting = false;
+
+
+ public BreakpointsListener() {
+ DebugPlugin.getDefault().getBreakpointManager().addBreakpointListener(this);
+ }
+
+ public void dispose() {
+ DebugPlugin.getDefault().getBreakpointManager().removeBreakpointListener(this);
+ }
+
+ public void startWaiting() {
+ fWaiting = true;
+ }
+ public void setTester(EventTester tester) {
+ fTester = tester;
+ startWaiting();
+ }
+
+ public void breakpointsAdded(IBreakpoint[] bps) {
+ Map<String,Object> emptyAttrs = Collections.emptyMap();
+ for (IBreakpoint bp : bps) {
+ if (fTester.checkEvent(EventType.ADDED, bp, emptyAttrs)) {
+ synchronized(this) {
+ fWaiting = false;
+ notifyAll();
+ }
+ return;
+ }
+ }
+ }
+
+ public void breakpointsRemoved(IBreakpoint[] bps, IMarkerDelta[] deltas) {
+ for (int i =0; i < bps.length; i++) {
+ Map<String,Object> attributes = Collections.emptyMap();
+ if (deltas[i] != null) {
+ attributes = deltas[i].getAttributes();
+ }
+ if (fTester.checkEvent(EventType.REMOVED, bps[i], attributes)) {
+ synchronized(this) {
+ fWaiting = false;
+ notifyAll();
+ }
+ return;
+ }
+ }
+ }
+
+ public void breakpointsChanged(IBreakpoint[] bps, IMarkerDelta[] deltas) {
+ for (int i =0; i < bps.length; i++) {
+ Map<String,Object> attributes = Collections.emptyMap();
+ if (deltas[i] != null) {
+ attributes = deltas[i].getAttributes();
+ }
+ if (fTester.checkEvent(EventType.CHANGED, bps[i], attributes)) {
+ synchronized(this) {
+ fWaiting = false;
+ notifyAll();
+ }
+ return;
+ }
+ }
+ }
+
+
+ public void waitForEvent() throws InterruptedException {
+ synchronized(this) {
+ while(fWaiting) {
+ wait();
+ }
+ }
+ }
+}
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
index 9594baa..58818af 100644
--- 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
@@ -1,115 +1,101 @@
package org.eclipse.tcf.debug.test;
-import java.util.Collection;
+import java.math.BigInteger;
import java.util.Map;
-import java.util.TreeMap;
import java.util.concurrent.ExecutionException;
import org.eclipse.cdt.debug.core.CDIDebugModel;
+import org.eclipse.cdt.debug.core.model.ICBreakpoint;
+import org.eclipse.cdt.debug.core.model.ICBreakpointType;
+import org.eclipse.cdt.debug.core.model.ICLineBreakpoint;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.debug.core.model.IBreakpoint;
+import org.eclipse.tcf.debug.test.BreakpointsListener.EventTester;
+import org.eclipse.tcf.debug.test.BreakpointsListener.EventType;
+import org.eclipse.tcf.debug.test.services.RunControlCM.ContextState;
import org.eclipse.tcf.debug.test.util.Transaction;
-import org.eclipse.tcf.services.IBreakpoints;
-import org.eclipse.tcf.services.IRunControl.RunControlContext;
+import org.eclipse.tcf.internal.debug.ui.launch.TCFLaunchContext;
+import org.eclipse.tcf.protocol.Protocol;
+import org.eclipse.tcf.services.ILineNumbers.CodeArea;
+import org.eclipse.tcf.services.ISymbols.Symbol;
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 BreakpointsListener fBpListener;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ fBpListener = new BreakpointsListener();
+
+ // CDT Breakpoint integration depends on the TCF-CDT breakpoint
+ // integration to be active. This is normally triggered by selecting
+ // a stack frame in the UI. Here force activation of the plugin
+ // artificially. None of the cdt integration packages are exported, so
+ // use the TCF Launch Context extension point indirectly to force the
+ // plugin to load.
+ TCFLaunchContext.getLaunchContext(null);
+ }
- 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();
+ @Override
+ protected void tearDown() throws Exception {
+ fBpListener.dispose();
+ super.tearDown();
}
-
- private void checkBreakpointForErrors(final String bpId, final String processId) throws InterruptedException, ExecutionException {
- new Transaction<Object>() {
+
+ private CodeArea getFunctionCodeArea(String functionName) throws Exception {
+ return new Transaction<CodeArea>() {
@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;
+ protected CodeArea process() throws InvalidCacheException, ExecutionException {
+ ContextState state = validate ( fRunControlCM.getState(fThreadId) );
+ String symId = validate ( fSymbolsCM.find(fProcessId, new BigInteger(state.pc), "tcf_test_func0") );
+ Symbol sym = validate ( fSymbolsCM.getContext(symId) );
+ CodeArea[] area = validate ( fLineNumbersCM.mapToSource(
+ fProcessId,
+ sym.getAddress(),
+ new BigInteger(sym.getAddress().toString()).add(BigInteger.valueOf(1))) );
+ return area[0];
}
- }.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();
+ private ICLineBreakpoint createLineBreakpoint(String file, int line) throws CoreException, ExecutionException, InterruptedException {
+ // Initiate wait for the context changed event.
+ final Object contextChangedWaitKey = new Object();
+ Protocol.invokeAndWait(new Runnable() { public void run() {
+ fBreakpointsCM.waitContextAdded(contextChangedWaitKey);
+ }});
- 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");
+ final ICLineBreakpoint bp = CDIDebugModel.createLineBreakpoint(file, ResourcesPlugin.getWorkspace().getRoot(), ICBreakpointType.REGULAR, line, true, 0, "", true);
+
+ Map<String, Object>[] addedBps = new Transaction<Map<String, Object>[]>() {
+ protected Map<String, Object>[] process() throws InvalidCacheException ,ExecutionException {
+ return validate(fBreakpointsCM.waitContextAdded(contextChangedWaitKey));
+ }
+
+ }.get();
+
+ fBpListener.setTester(new EventTester() {
+ public boolean checkEvent(EventType type, IBreakpoint testBp, Map<String, Object> deltaAttributes) {
+ return (type == EventType.CHANGED && bp == testBp);
+ }
+ });
+ fBpListener.waitForEvent();
+ Assert.assertEquals(1, addedBps.length);
+ Assert.assertEquals(1, bp.getMarker().getAttribute(ICBreakpoint.INSTALL_COUNT, -1));
- //CDIDebugModel.createFunctionBreakpoint();
+ return bp;
+ }
+
+ public void testContextAddedOnLineBrakpointCreate() throws Exception {
+ initProcessModel("tcf_test_func0");
- checkBreakpointForErrors(bpId, fProcessId);
+ CodeArea bpCodeArea = getFunctionCodeArea("tcf_test_func0");
+ ICLineBreakpoint bp = createLineBreakpoint(bpCodeArea.file, bpCodeArea.start_line);
}
+
}
diff --git a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/CacheTests.java b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/CacheTests.java
new file mode 100644
index 0000000..5021e49
--- /dev/null
+++ b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/CacheTests.java
@@ -0,0 +1,832 @@
+/***********************************************s********************************
+ * Copyright (c) 2006 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.ArrayList;
+import java.util.List;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.ExecutionException;
+
+import junit.framework.Assert;
+import junit.framework.TestCase;
+
+import org.eclipse.tcf.debug.test.util.Callback;
+import org.eclipse.tcf.debug.test.util.CallbackCache;
+import org.eclipse.tcf.debug.test.util.DataCallback;
+import org.eclipse.tcf.debug.test.util.Query;
+import org.eclipse.tcf.protocol.Protocol;
+
+/**
+ * Tests that exercise the DataCache object.
+ */
+public class CacheTests extends TestCase {
+
+ TestCache fTestCache;
+ DataCallback<Integer> fRetrieveRm;
+
+ class TestCache extends CallbackCache<Integer> {
+
+ @Override
+ protected void retrieve(DataCallback<Integer> rm) {
+ synchronized(CacheTests.this) {
+ fRetrieveRm = rm;
+ CacheTests.this.notifyAll();
+ }
+ }
+
+ @Override
+ protected void handleCompleted(Integer data, Throwable error, boolean canceled) {
+ // TODO Auto-generated method stub
+ super.handleCompleted(data, error, canceled);
+ }
+
+ }
+
+ class TestQuery extends Query<Integer> {
+ @Override
+ protected void execute(final DataCallback<Integer> rm) {
+ fTestCache.update(new DataCallback<Integer>(rm) {
+ @Override
+ protected void handleSuccess() {
+ rm.setData(fTestCache.getData());
+ rm.done();
+ }
+ });
+ }
+ }
+
+ /**
+ * There's no rule on how quickly the cache has to start data retrieval
+ * after it has been requested. It could do it immediately, or it could
+ * wait a dispatch cycle, etc..
+ */
+ private void waitForRetrieveRm() {
+ synchronized(this) {
+ while (fRetrieveRm == null) {
+ try {
+ wait();
+ } catch (InterruptedException e) {
+ return;
+ }
+ }
+ }
+ }
+
+ public void setUp() throws ExecutionException, InterruptedException {
+ fTestCache = new TestCache();
+ }
+
+ public void tearDown() throws ExecutionException, InterruptedException {
+ fRetrieveRm = null;
+ fTestCache = null;
+ }
+
+ private void assertCacheValidWithData(Object data) {
+ Assert.assertTrue(fTestCache.isValid());
+ Assert.assertEquals(data, fTestCache.getData());
+ Assert.assertNull(fTestCache.getError());
+ }
+
+ private void assertCacheResetWithoutData() {
+ Assert.assertFalse(fTestCache.isValid());
+ try {
+ fTestCache.getData();
+ Assert.fail("Expected an IllegalStateException");
+ } catch (IllegalStateException e) {}
+ try {
+ fTestCache.getError();
+ Assert.fail("Expected an IllegalStateException");
+ } catch (IllegalStateException e) {}
+ }
+
+ private void assertCacheValidWithoutData() {
+ Assert.assertTrue(fTestCache.isValid());
+ Assert.assertEquals(null, fTestCache.getData());
+ Assert.assertNotNull(fTestCache.getError());
+ Assert.assertEquals(fTestCache.getError(), ERROR_TARGET_RUNNING);
+ }
+
+ private void assertCacheWaiting() {
+ Assert.assertFalse(fTestCache.isValid());
+ try {
+ fTestCache.getData();
+ Assert.fail("Expected an IllegalStateException");
+ } catch (IllegalStateException e) {}
+ try {
+ fTestCache.getError();
+ Assert.fail("Expected an IllegalStateException");
+ } catch (IllegalStateException e) {}
+ Assert.assertFalse(fRetrieveRm.isCanceled());
+ }
+
+ private void assertCacheInvalidAndWithCanceledRM() {
+ Assert.assertFalse(fTestCache.isValid());
+ try {
+ fTestCache.getData();
+ Assert.fail("Expected an IllegalStateException");
+ } catch (IllegalStateException e) {}
+ try {
+ fTestCache.getError();
+ Assert.fail("Expected an IllegalStateException");
+ } catch (IllegalStateException e) {}
+ Assert.assertTrue(fRetrieveRm.isCanceled());
+ }
+
+ public void testGet() throws InterruptedException, ExecutionException {
+ // Request data from cache
+ Query<Integer> q = new TestQuery();
+
+ // Check initial state
+ Assert.assertFalse(fTestCache.isValid());
+
+ q.invoke();
+
+ // Wait until the cache requests the data.
+ waitForRetrieveRm();
+
+ // Check state while waiting for data
+ Assert.assertFalse(fTestCache.isValid());
+
+ // Complete the cache's retrieve data request.
+ Protocol.invokeAndWait(new Runnable() {
+ public void run() {
+ fRetrieveRm.setData(1);
+ fRetrieveRm.done();
+
+ // Check that the data is available in the cache immediately
+ // (in the same dispatch cycle).
+ Assert.assertEquals(1, (int)fTestCache.getData());
+ Assert.assertTrue(fTestCache.isValid());
+ }
+ });
+
+ Assert.assertEquals(1, (int)q.get());
+
+ // Re-check final state
+ assertCacheValidWithData(1);
+ }
+
+ public void testGetWithCompletionDelay() throws InterruptedException, ExecutionException {
+ // Check initial state
+ Assert.assertFalse(fTestCache.isValid());
+
+ // Request data from cache
+ Query<Integer> q = new TestQuery();
+ q.invoke();
+
+ // Wait until the cache starts data retrieval.
+ waitForRetrieveRm();
+
+ // Check state while waiting for data
+ Assert.assertFalse(fTestCache.isValid());
+
+ // Set the data to the callback
+ Protocol.invokeLater(
+ 100,
+ new Runnable() {
+ public void run() {
+ fRetrieveRm.setData(1);
+ fRetrieveRm.done();
+
+ }
+ });
+
+ Assert.assertEquals(1, (int)q.get());
+
+ // Check final state
+ assertCacheValidWithData(1);
+ }
+
+ public void testGetWithTwoClients() throws InterruptedException, ExecutionException {
+ // Check initial state
+ Assert.assertFalse(fTestCache.isValid());
+
+ // Request data from cache
+ Query<Integer> q1 = new TestQuery();
+ q1.invoke();
+
+ // Request data from cache again
+ Query<Integer> q2 = new TestQuery();
+ q2.invoke();
+
+ // Wait until the cache starts data retrieval.
+ waitForRetrieveRm();
+
+ // Check state while waiting for data
+ Assert.assertFalse(fTestCache.isValid());
+
+ // Set the data to the callback
+ Protocol.invokeLater(new Runnable() {
+ public void run() {
+ fRetrieveRm.setData(1);
+ fRetrieveRm.done();
+
+ }
+ });
+
+ Assert.assertEquals(1, (int)q1.get());
+ Assert.assertEquals(1, (int)q2.get());
+
+ // Check final state
+ assertCacheValidWithData(1);
+ }
+
+ public void testGetWithManyClients() throws InterruptedException, ExecutionException {
+ // Check initial state
+ Assert.assertFalse(fTestCache.isValid());
+
+ // Request data from cache
+ List<Query<Integer>> qList = new ArrayList<Query<Integer>>();
+ for (int i = 0; i < 10; i++) {
+ Query<Integer> q = new TestQuery();
+ q.invoke();
+ qList.add(q);
+ }
+ // Wait until the cache starts data retrieval.
+ waitForRetrieveRm();
+
+ // Check state while waiting for data
+ Assert.assertFalse(fTestCache.isValid());
+
+ // Set the data to the callback
+ Protocol.invokeLater(new Runnable() {
+ public void run() {
+ fRetrieveRm.setData(1);
+ fRetrieveRm.done();
+
+ }
+ });
+
+ for (Query<Integer> q : qList) {
+ Assert.assertEquals(1, (int)q.get());
+ }
+
+ // Check final state
+ assertCacheValidWithData(1);
+ }
+
+ private static final Exception ERROR_TARGET_RUNNING = new Exception("Target is running");
+
+ // DISABLE TESTS
+ //
+ // We say a cache is "disabled" when its most recent attempt to update from
+ // the source failed. Also, a cache may make itself disabled as a reaction
+ // to a state change notification from its source (e.g., the target
+ // resumed). In either case, the cache is in the valid state but it has no
+ // data and the status reflects an error. Keep in mind that the 'valid'
+ // state is not a reflection of the quality of the data, but merely whether
+ // the cache object's representation of the data is stale or
+ // not. A transaction that uses a "disabled" cache object will simply fail;
+ // it will not ask the cache to update its data from the source. Only a
+ // change in the source's state would cause the cache to put itself back in
+ // the invalid state, thus opening the door to another update.
+
+ /**
+ * Test behavior when a cache object is asked to update itself after it has
+ * become "disabled". Since a "disabled" cache is in the valid state, a
+ * request for it to update from the source should be ignored. However, the
+ * client callback is not completed until next state change in cache.
+ */
+ public void testDisableBeforeRequest() throws InterruptedException, ExecutionException {
+ // Disable the cache
+ Protocol.invokeAndWait(new Runnable() {
+ public void run() {
+ fTestCache.set(null, ERROR_TARGET_RUNNING, true);
+ }
+ });
+
+ assertCacheValidWithoutData();
+
+ // Try to request data from cache
+ Query<Integer> q = new TestQuery();
+ q.invoke();
+
+ Thread.sleep(100);
+
+ // Retrieval should never have been made.
+ Assert.assertEquals(null, fRetrieveRm);
+
+ // Disable the cache. This should trigger the qery to complete.
+ Protocol.invokeAndWait(new Runnable() {
+ public void run() {
+ fTestCache.set(null, new Throwable("Cache invalid"), false);
+ }
+ });
+
+ // The cache has no data so the query should have failed
+ try {
+ q.get();
+ Assert.fail("expected an exeption");
+ } catch (ExecutionException e) {
+ // expected the exception
+ }
+ }
+
+ /**
+ * Test behavior when a cache object goes into the "disabled" state while an
+ * update request is ongoing. The subsequent completion of the request should
+ * have no effect on the cache
+ */
+ public void testDisableWhilePending() throws InterruptedException, ExecutionException {
+ // Request data from cache
+ Query<Integer> q = new TestQuery();
+ q.invoke();
+
+ // Disable the cache
+ Protocol.invokeAndWait(new Runnable() {
+ public void run() {
+ fTestCache.set(null, ERROR_TARGET_RUNNING, true);
+ }
+ });
+
+ assertCacheValidWithoutData();
+
+ // Complete the retrieve RM. Note that the disabling of the cache above
+ // disassociates it from its retrieval RM. Thus regardless of how that
+ // request completes, it does not affect the cache.
+ Protocol.invokeAndWait(new Runnable() {
+ public void run() {
+ fRetrieveRm.setData(1);
+ fRetrieveRm.done();
+ }
+ });
+
+ // Validate that cache is still disabled without data.
+ assertCacheValidWithoutData();
+ }
+
+ /**
+ * Test behavior when a cache object goes into the "disabled" state while
+ * it's in the valid state. The cache remains in the valid state but it
+ * loses its data and obtains an error status.
+ */
+ public void testDisableWhileValid() throws InterruptedException, ExecutionException {
+ // Request data from cache
+ Query<Integer> q = new TestQuery();
+ q.invoke();
+
+ // Wait until the cache starts data retrieval.
+ waitForRetrieveRm();
+
+ // Complete the request
+ Protocol.invokeAndWait(new Runnable() {
+ public void run() {
+ fRetrieveRm.setData(1);
+ fRetrieveRm.done();
+ }
+ });
+
+ Assert.assertEquals(Integer.valueOf(1), q.get());
+
+ // Disable the cache
+ Protocol.invokeAndWait(new Runnable() {
+ public void run() {
+ fTestCache.set(null, ERROR_TARGET_RUNNING, true);
+ }
+ });
+
+ // Check final state
+ assertCacheValidWithoutData();
+ }
+
+ public void testSetWithValue() throws InterruptedException, ExecutionException {
+ // Disable the cache
+ Protocol.invokeAndWait(new Runnable() {
+ public void run() {
+ fTestCache.set(2, null, true);
+ }
+ });
+
+ // Validate that cache is disabled without data.
+ assertCacheValidWithData(2);
+ }
+
+
+ public void testCancelWhilePending() throws InterruptedException, ExecutionException {
+ // Request data from cache
+ Query<Integer> q = new TestQuery();
+ q.invoke();
+
+ // Wait until the cache starts data retrieval.
+ waitForRetrieveRm();
+
+ // Cancel the client request
+ q.cancel(true);
+ try {
+ q.get();
+ Assert.fail("Expected a cancellation exception");
+ } catch (CancellationException e) {} // Expected exception;
+
+ assertCacheInvalidAndWithCanceledRM();
+
+ // Simulate the retrieval completing successfully despite the cancel
+ // request. Perhaps the retrieval logic isn't checking the RM status.
+ // Even if it is checking, it may have gotten passed its last checkpoint
+ Protocol.invokeAndWait(new Runnable() {
+ public void run() {
+ fRetrieveRm.setData(1);
+ fRetrieveRm.done();
+ }
+ });
+
+ // Validate that cache didn't accept the result after its RM was canceled
+ assertCacheInvalidAndWithCanceledRM();
+ }
+
+ public void testCancelWhilePending2() throws InterruptedException, ExecutionException {
+ // Request data from cache
+ Query<Integer> q = new TestQuery();
+ q.invoke();
+
+ // Wait until the cache starts data retrieval.
+ waitForRetrieveRm();
+
+ // Cancel the client request
+ q.cancel(true);
+ try {
+ q.get();
+ Assert.fail("Expected a cancellation exception");
+ } catch (CancellationException e) {} // Expected exception;
+
+ assertCacheInvalidAndWithCanceledRM();
+
+ // Simulate retrieval logic that is regularly checking the RM's cancel
+ // status and has discovered that the request has been canceled. It
+ // technically does not need to explicitly set a cancel status object in
+ // the RM, thanks to RequestMonitor.getStatus() automatically returning
+ // Status.CANCEL_STATUS when its in the cancel state. So here we
+ // simulate the retrieval logic just aborting its operations and
+ // completing the RM. Note that it hasn't provided the data to the
+ // cache.
+ Protocol.invokeAndWait(new Runnable() {
+ public void run() {
+ fRetrieveRm.done();
+ }
+ });
+
+ assertCacheInvalidAndWithCanceledRM();
+ }
+
+ public void testCancelWhilePending3() throws InterruptedException, ExecutionException {
+ // Request data from cache
+ Query<Integer> q = new TestQuery();
+ q.invoke();
+
+ // Wait until the cache starts data retrieval.
+ waitForRetrieveRm();
+
+ // Cancel the client request
+ q.cancel(true);
+ try {
+ q.get();
+ Assert.fail("Expected a cancellation exception");
+ } catch (CancellationException e) {} // Expected exception;
+
+ assertCacheInvalidAndWithCanceledRM();
+
+ // Simulate retrieval logic that is regularly checking the RM's cancel
+ // status and has discovered that the request has been canceled. It
+ // aborts its processing, sets STATUS.CANCEL_STATUS in the RM and
+ // completes it.
+ Protocol.invokeAndWait(new Runnable() {
+ public void run() {
+ fRetrieveRm.setError(new CancellationException());
+ fRetrieveRm.done();
+ }
+ });
+
+ // Validate that cache didn't accept the result after its RM was canceled
+ assertCacheInvalidAndWithCanceledRM();
+ }
+
+ public void testCancelWhilePendingWithoutClientNotification() throws InterruptedException, ExecutionException {
+ final boolean canceledCalled[] = new boolean[] { false };
+
+ fTestCache = new TestCache() {
+ protected synchronized void canceled() {
+ canceledCalled[0] = true;
+ };
+ };
+
+ // Request data from cache
+ Query<Integer> q = new Query<Integer>() {
+ @Override
+ protected void execute(final DataCallback<Integer> rm) {
+
+ fTestCache.update(new Callback(rm) {
+ @Override
+ public synchronized void addCancelListener(ICanceledListener listener) {
+ // Do not add the cancel listener so that the cancel request is not
+ // propagated to the cache.
+ }
+
+ @Override
+ protected void handleSuccess() {
+ rm.setData(fTestCache.getData());
+ rm.done();
+ }
+ });
+ }
+ };
+ q.invoke();
+
+ // Wait until the cache starts data retrieval.
+ waitForRetrieveRm();
+
+ // Cancel the client request
+ q.cancel(true);
+
+ assertCacheInvalidAndWithCanceledRM();
+
+ // AbstractCache.canceled() should be called after isCanceled()
+ // discovers that the client has canceled its request. The canceled() method is
+ // called in a separate dispatch cycle, so we have to wait one cycle of the executor
+ // after is canceled is called.
+ fRetrieveRm.isCanceled();
+ Protocol.invokeAndWait(new Runnable() { public void run() {} });
+ Assert.assertTrue(canceledCalled[0]);
+
+ try {
+ q.get();
+ Assert.fail("Expected a cancellation exception");
+ } catch (CancellationException e) {} // Expected exception;
+
+
+ // Completed the retrieve RM
+ Protocol.invokeAndWait(new Runnable() {
+ public void run() {
+ fRetrieveRm.setData(1);
+ fRetrieveRm.done();
+ }
+ });
+
+ // Validate that cache didn't accept the result after its RM was canceled
+ assertCacheInvalidAndWithCanceledRM();
+ }
+
+ /**
+ * This test forces a race condition where a client that requested data
+ * cancels. While shortly after a second client starts a new request.
+ * The first request's cancel should not interfere with the second
+ * request.
+ */
+ public void testCancelAfterCompletedRaceCondition() throws InterruptedException, ExecutionException {
+
+ // Create a client request with a badly behaved cancel implementation.
+ final Callback[] rmBad = new Callback[1] ;
+ final boolean qBadCanceled[] = new boolean[] { false };
+ Query<Integer> qBad = new Query<Integer>() {
+ @Override
+ protected void execute(final DataCallback<Integer> rm) {
+ rmBad[0] = new Callback(rm) {
+ @Override
+ public synchronized void removeCancelListener(ICanceledListener listener) {
+ // Do not add the cancel listener so that the cancel request is not
+ // propagated to the cache.
+ }
+
+ @Override
+ public void cancel() {
+ if (qBadCanceled[0]) {
+ super.cancel();
+ }
+ }
+
+ @Override
+ public synchronized boolean isCanceled() {
+ return qBadCanceled[0];
+ }
+
+ @Override
+ public synchronized void done() {
+ // Avoid clearing cancel listeners list
+ };
+
+ @Override
+ protected void handleSuccess() {
+ rm.setData(fTestCache.getData());
+ rm.done();
+ };
+ };
+
+ fTestCache.update(rmBad[0]);
+ }
+ };
+ qBad.invoke();
+
+ // Wait until the cache starts data retrieval.
+ waitForRetrieveRm();
+
+ // Reset the cache
+ Protocol.invokeAndWait(new Runnable() {
+ public void run() {
+ fRetrieveRm = null;
+ fTestCache.set(null, null, true);
+ fTestCache.reset();
+ }
+ });
+
+ Query<Integer> qGood = new TestQuery();
+ qGood.invoke();
+
+ // Wait until the cache starts data retrieval.
+ waitForRetrieveRm();
+
+ qBadCanceled[0] = true;
+ rmBad[0].cancel();
+
+ Assert.assertFalse(fRetrieveRm.isCanceled());
+
+ // Completed the retrieve RM
+ Protocol.invokeAndWait(new Runnable() {
+ public void run() {
+ fRetrieveRm.setData(1);
+ fRetrieveRm.done();
+ }
+ });
+
+ qGood.get();
+
+ assertCacheValidWithData(1);
+ }
+
+ public void testCancelWhilePendingWithTwoClients() throws InterruptedException, ExecutionException {
+
+ // Request data from cache. Use an additional invokeAndWait to
+ // ensure both update requests are initiated before we wait
+ // for retrieval to start
+ Query<Integer> q1 = new TestQuery();
+ q1.invoke();
+ Protocol.invokeAndWait(new Runnable() { public void run() {} });
+
+ // Request data from cache again
+ Query<Integer> q2 = new TestQuery();
+ q2.invoke();
+ Protocol.invokeAndWait(new Runnable() { public void run() {} });
+
+ // Wait until the cache starts data retrieval.
+ waitForRetrieveRm();
+
+ // Cancel the first client request
+ q1.cancel(true);
+ try {
+ q1.get();
+ Assert.fail("Expected a cancellation exception");
+ } catch (CancellationException e) {} // Expected exception;
+ assertCacheWaiting();
+
+ // Cancel the second request
+ q2.cancel(true);
+ try {
+ q2.get();
+ Assert.fail("Expected a cancellation exception");
+ } catch (CancellationException e) {} // Expected exception;
+
+ assertCacheInvalidAndWithCanceledRM();
+
+ // Completed the retrieve RM
+ Protocol.invokeAndWait(new Runnable() {
+ public void run() {
+ fRetrieveRm.setData(1);
+ fRetrieveRm.done();
+ }
+ });
+
+ // Validate that cache didn't accept the result after its RM was canceled
+ assertCacheInvalidAndWithCanceledRM();
+ }
+
+ public void testCancelWhilePendingWithManyClients() throws InterruptedException, ExecutionException {
+ // Request data from cache
+ List<Query<Integer>> qList = new ArrayList<Query<Integer>>();
+ for (int i = 0; i < 10; i++) {
+ Query<Integer> q = new TestQuery();
+ q.invoke();
+ Protocol.invokeAndWait(new Runnable() { public void run() {} });
+ qList.add(q);
+ }
+
+ // Wait until the cache starts data retrieval.
+ waitForRetrieveRm();
+
+ // Cancel some client requests
+ int[] toCancel = new int[] { 0, 2, 5, 9};
+ for (int i = 0; i < toCancel.length; i++) {
+
+ // Cancel request and verify that its canceled
+ Query<Integer> q = qList.get(toCancel[i]);
+ q.cancel(true);
+ try {
+ q.get();
+ Assert.fail("Expected a cancellation exception");
+ } catch (CancellationException e) {} // Expected exception;
+ qList.set(toCancel[i], null);
+
+ assertCacheWaiting();
+ }
+
+ // Replace canceled requests with new ones
+ for (int i = 0; i < toCancel.length; i++) {
+ Query<Integer> q = new TestQuery();
+ q.invoke();
+ Protocol.invokeAndWait(new Runnable() { public void run() {} });
+ qList.set(toCancel[i], q);
+ assertCacheWaiting();
+ }
+
+ // Now cancel all requests
+ for (int i = 0; i < (qList.size() - 1); i++) {
+ // Validate that cache is still waiting and is not canceled
+ assertCacheWaiting();
+ qList.get(i).cancel(true);
+ }
+ qList.get(qList.size() - 1).cancel(true);
+ assertCacheInvalidAndWithCanceledRM();
+
+ // Completed the retrieve RM
+ Protocol.invokeAndWait(new Runnable() {
+ public void run() {
+ fRetrieveRm.setData(1);
+ fRetrieveRm.done();
+ }
+ });
+
+ // Validate that cache didn't accept the result after its RM was canceled
+ assertCacheInvalidAndWithCanceledRM();
+ }
+
+ public void testResetWhileValid() throws InterruptedException, ExecutionException {
+ // Request data from cache
+ Query<Integer> q = new TestQuery();
+ q.invoke();
+
+ // Wait until the cache starts data retrieval.
+ waitForRetrieveRm();
+
+ Protocol.invokeAndWait(new Runnable() {
+ public void run() {
+ fRetrieveRm.setData(1);
+ fRetrieveRm.done();
+ }
+ });
+
+ q.get();
+
+ // Disable cache
+ Protocol.invokeAndWait(new Runnable() {
+ public void run() {
+ fTestCache.reset();
+ }
+ });
+
+ // Check final state
+ assertCacheResetWithoutData();
+ }
+
+ public void testSetAndReset() throws InterruptedException, ExecutionException {
+ fTestCache = new TestCache() {
+ @Override
+ protected void handleCompleted(Integer data, Throwable error, boolean canceled) {
+ if (!canceled) {
+ // USE 'false' for valid argument. Cache should be left in
+ // invalid state.
+ set(data, error, false);
+ }
+ }
+ };
+
+ // Request data from cache
+ Query<Integer> q = new TestQuery();
+ q.invoke();
+
+ // Wait until the cache starts data retrieval.
+ waitForRetrieveRm();
+
+ Protocol.invokeAndWait(new Runnable() {
+ public void run() {
+ fRetrieveRm.setData(1);
+ fRetrieveRm.done();
+ }
+ });
+
+ // Query should complete with the data from request monitor.
+ try {
+ q.get();
+ Assert.fail("Expected InvalidCacheException");
+ } catch(ExecutionException e) {}
+
+ // No need to disable cache, it should already be disabled.
+
+ // Check final state
+ assertCacheResetWithoutData();
+ }
+
+} \ No newline at end of file
diff --git a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/QueryTests.java b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/QueryTests.java
new file mode 100644
index 0000000..1c790c7
--- /dev/null
+++ b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/QueryTests.java
@@ -0,0 +1,255 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 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;
+
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import junit.framework.Assert;
+import junit.framework.TestCase;
+
+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.Query;
+import org.eclipse.tcf.protocol.Protocol;
+import org.junit.Test;
+
+/**
+ * Tests that exercise the Query object.
+ */
+public class QueryTests extends TestCase{
+
+ public void testSimpleGet() throws InterruptedException, ExecutionException {
+ Query<Integer> q = new Query<Integer>() {
+ @Override
+ protected void execute(DataCallback<Integer> rm) {
+ rm.setData(1);
+ rm.done();
+ }
+ };
+ // Check initial state
+ Assert.assertTrue(!q.isDone());
+ Assert.assertTrue(!q.isCancelled());
+
+ q.invoke();
+ Assert.assertEquals(1, (int)q.get());
+
+ // Check final state
+ Assert.assertTrue(q.isDone());
+ Assert.assertTrue(!q.isCancelled());
+
+ }
+
+ public void testGetError() throws InterruptedException, ExecutionException {
+ final String error_message = "Test Error";
+
+ Query<Integer> q = new Query<Integer>() {
+ @Override
+ protected void execute(DataCallback<Integer> rm) {
+ rm.setError(new Throwable(error_message)); //$NON-NLS-1$
+ rm.done();
+ }
+ };
+
+ // Check initial state
+ Assert.assertTrue(!q.isDone());
+ Assert.assertTrue(!q.isCancelled());
+
+ q.invoke();
+
+ try {
+ q.get();
+ Assert.fail("Expected exception");
+ } catch (ExecutionException e) {
+ Assert.assertEquals(e.getCause().getMessage(), error_message);
+ }
+
+ // Check final state
+ Assert.assertTrue(q.isDone());
+ Assert.assertTrue(!q.isCancelled());
+
+ }
+
+ public void testGetWithMultipleDispatches() throws InterruptedException, ExecutionException {
+ Query<Integer> q = new Query<Integer>() {
+ @Override
+ protected void execute(final DataCallback<Integer> rm) {
+ Protocol.invokeLater(new Runnable() {
+ public void run() {
+ rm.setData(1);
+ rm.done();
+ }
+ @Override
+ public String toString() { return super.toString() + "\n getWithMultipleDispatchesTest() second runnable"; } //$NON-NLS-1$
+ });
+ }
+ @Override
+ public String toString() { return super.toString() + "\n getWithMultipleDispatchesTest() first runnable (query)"; } //$NON-NLS-1$
+ };
+ q.invoke();
+ Assert.assertEquals(1, (int)q.get());
+ }
+
+ public void testExceptionOnGet() throws InterruptedException, ExecutionException {
+ Query<Integer> q = new Query<Integer>() {
+ @Override
+ protected void execute(final DataCallback<Integer> rm) {
+ rm.setError(new Throwable("")); //$NON-NLS-1$
+ rm.done();
+ }
+ };
+
+ q.invoke();
+
+ try {
+ q.get();
+ Assert.fail("Excpected ExecutionExeption");
+ } catch (ExecutionException e) {
+ } finally {
+ Assert.assertTrue(q.isDone());
+ Assert.assertTrue(!q.isCancelled());
+ }
+ }
+
+ public void testCancelBeforeWaiting() throws InterruptedException, ExecutionException {
+ final Query<Integer> q = new Query<Integer>() {
+ @Override protected void execute(final DataCallback<Integer> rm) {
+ Assert.fail("Query was cancelled, it should not be called."); //$NON-NLS-1$
+ rm.done();
+ }
+ };
+
+ // Cancel before invoking the query.
+ q.cancel(false);
+
+ Assert.assertTrue(q.isDone());
+ Assert.assertTrue(q.isCancelled());
+
+ // Start the query.
+ q.invoke();
+
+
+
+ // Block to retrieve data
+ try {
+ q.get();
+ } catch (CancellationException e) {
+ return; // Success
+ } finally {
+ Assert.assertTrue(q.isDone());
+ Assert.assertTrue(q.isCancelled());
+ }
+ Assert.assertTrue("CancellationException should have been thrown", false); //$NON-NLS-1$
+ }
+
+ public void testCancelWhileWaiting() throws InterruptedException, ExecutionException {
+ final DataCallback<?>[] rmHolder = new DataCallback<?>[1];
+ final Boolean[] cancelCalled = new Boolean[] { Boolean.FALSE };
+
+ final Query<Integer> q = new Query<Integer>() {
+ @Override protected void execute(final DataCallback<Integer> rm) {
+ synchronized (rmHolder) {
+ rmHolder[0] = rm;
+ rmHolder.notifyAll();
+ }
+ }
+ };
+
+ // Start the query.
+ q.invoke();
+
+ // Wait until the query is started
+ synchronized (rmHolder) {
+ while(rmHolder[0] == null) {
+ rmHolder.wait();
+ }
+ }
+
+ // Add a cancel listener to the query RM
+ rmHolder[0].addCancelListener(new ICanceledListener() {
+
+ public void requestCanceled(Callback rm) {
+ cancelCalled[0] = Boolean.TRUE;
+ }
+ });
+
+ // Cancel running request.
+ q.cancel(false);
+
+ Assert.assertTrue(cancelCalled[0]);
+ Assert.assertTrue(rmHolder[0].isCanceled());
+ Assert.assertTrue(q.isCancelled());
+ Assert.assertTrue(q.isDone());
+
+ // Retrieve data
+ try {
+ q.get();
+ } catch (CancellationException e) {
+ return; // Success
+ } finally {
+ Assert.assertTrue(q.isDone());
+ Assert.assertTrue(q.isCancelled());
+ }
+
+ // Complete rm and query.
+ @SuppressWarnings("unchecked")
+ DataCallback<Integer> drm = (DataCallback<Integer>)rmHolder[0];
+ drm.setData(new Integer(1));
+ rmHolder[0].done();
+
+ // Try to retrieve data again, it should still result in
+ // cancellation exception.
+ try {
+ q.get();
+ } catch (CancellationException e) {
+ return; // Success
+ } finally {
+ Assert.assertTrue(q.isDone());
+ Assert.assertTrue(q.isCancelled());
+ }
+
+
+ Assert.assertTrue("CancellationException should have been thrown", false); //$NON-NLS-1$
+ }
+
+
+ public void testGetTimeout() throws InterruptedException, ExecutionException {
+ final Query<Integer> q = new Query<Integer>() {
+ @Override
+ protected void execute(final DataCallback<Integer> rm) {
+ // Call done with a delay of 1 second, to avoid stalling the tests.
+ Protocol.invokeLater(
+ 60000,
+ new Runnable() {
+ public void run() { rm.done(); }
+ });
+ }
+ };
+
+ q.invoke();
+
+ // Note: no point in checking isDone() and isCancelled() here, because
+ // the value could change on timing.
+
+ try {
+ q.get(1, TimeUnit.MILLISECONDS);
+ } catch (TimeoutException e) {
+ return; // Success
+ } finally {
+ Assert.assertFalse("Query should not be done yet, it should have timed out first.", q.isDone()); //$NON-NLS-1$
+ }
+ Assert.assertTrue("TimeoutException should have been thrown", false); //$NON-NLS-1$
+ }
+
+}
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 7173f26..4c49aa4 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
@@ -3,23 +3,15 @@ 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.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.ILineNumbers.CodeArea;
import org.eclipse.tcf.services.IRunControl;
import org.eclipse.tcf.services.IRunControl.RunControlContext;
import org.eclipse.tcf.services.ISymbols;
@@ -27,194 +19,11 @@ import org.eclipse.tcf.services.ISymbols.Symbol;
import org.junit.Assert;
@SuppressWarnings("restriction")
-public class SampleTest extends AbstractTcfUITest
-{
- private String fTestId;
- private RunControlContext fTestCtx;
- private String fProcessId = "";
- private String fThreadId = "";
- private RunControlContext fThreadCtx;
+public class SampleTest extends AbstractTcfUITest {
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- clearBreakpoints();
- }
-
- 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();
- }
-
- 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 {
-
- // 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 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 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();
- }
-
- private void initProcessModel(String bpId, String testFunc) throws Exception {
- createBreakpoint(bpId, testFunc);
- fDebugViewListener.reset();
-
- 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 {
- String bpId = "TestStepBP";
- initProcessModel(bpId, "tcf_test_func0");
+ initProcessModel("tcf_test_func0");
VirtualItem launchItem = fDebugViewListener.findElement(new Pattern[] { Pattern.compile(".*" + fLaunch.getLaunchConfiguration().getName() + ".*") } );
Assert.assertTrue(launchItem != null);
@@ -227,13 +36,10 @@ public class SampleTest extends AbstractTcfUITest
VirtualItem frameItem = fDebugViewListener.findElement(threadItem, new Pattern[] { Pattern.compile(".*tcf_test_func0.*")});
Assert.assertTrue(frameItem != null);
-
- checkBreakpointForErrors(bpId, fProcessId);
}
public void testSteppingDebugViewOnly() throws Exception {
- String bpId = "TestStepBP";
- initProcessModel(bpId, "tcf_test_func0");
+ initProcessModel("tcf_test_func0");
// Execute step loop
String previousThreadLabel = null;
@@ -250,15 +56,13 @@ public class SampleTest extends AbstractTcfUITest
Assert.assertTrue(!topFrameLabel.equals(previousThreadLabel));
previousThreadLabel = topFrameLabel;
}
-
- checkBreakpointForErrors(bpId, fProcessId);
}
public void testSteppingWithVariablesAndRegisters() throws Exception {
fVariablesViewViewer.setActive(true);
fRegistersViewViewer.setActive(true);
- initProcessModel("TestStepBP", "tcf_test_func0");
+ initProcessModel("tcf_test_func0");
// Execute step loop
String previousThreadLabel = null;
@@ -279,14 +83,10 @@ public class SampleTest extends AbstractTcfUITest
Assert.assertTrue(!topFrameLabel.equals(previousThreadLabel));
previousThreadLabel = topFrameLabel;
}
-
- checkBreakpointForErrors("TestStepBP", fProcessId);
}
public void testSymbolsCMResetOnContextRemove() throws Exception {
- createBreakpoint("TestStepBP", "tcf_test_func0");
- startProcess();
- runToTestEntry("tcf_test_func0");
+ initProcessModel("tcf_test_func0");
// Retrieve the current PC for use later
final String pc = new Transaction<String>() {
@@ -343,10 +143,75 @@ public class SampleTest extends AbstractTcfUITest
}.get();
}
+ public void testLineNumbersCMResetOnContextRemove() throws Exception {
+ initProcessModel("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();
+
+ final BigInteger pcNumber = new BigInteger(pc);
+ final BigInteger pcNumberPlusOne = pcNumber.add(BigInteger.valueOf(1));
+
+ // Retrieve the line number for current PC.
+ final CodeArea[] pcCodeAreas = new Transaction<CodeArea[]>() {
+ @Override
+ protected CodeArea[] process() throws InvalidCacheException, ExecutionException {
+ CodeArea[] areas = validate(fLineNumbersCM.mapToSource(fProcessId, pcNumber, pcNumberPlusOne));
+ Assert.assertNotNull(areas);
+ Assert.assertTrue(areas.length != 0);
+
+ areas = validate(fLineNumbersCM.mapToSource(fThreadId, pcNumber, pcNumberPlusOne));
+ Assert.assertNotNull(areas);
+ Assert.assertTrue(areas.length != 0);
+
+ CodeArea[] areas2 = validate(fLineNumbersCM.mapToMemory(fProcessId, areas[0].file, areas[0].start_line, areas[0].start_column));
+ Assert.assertNotNull(areas2);
+ Assert.assertTrue(areas2.length != 0);
+
+ areas2 = validate(fLineNumbersCM.mapToMemory(fThreadId, areas[0].file, areas[0].start_line, areas[0].start_column));
+ Assert.assertNotNull(areas2);
+ Assert.assertTrue(areas2.length != 0);
+
+ return areas;
+ }
+ }.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(fLineNumbersCM.mapToSource(fProcessId, pcNumber, pcNumberPlusOne));
+ Assert.fail("Expected error");
+ } catch (ExecutionException e) {}
+ try {
+ validate(fLineNumbersCM.mapToSource(fThreadId, pcNumber, pcNumberPlusOne));
+ Assert.fail("Expected error");
+ } catch (ExecutionException e) {}
+ try {
+ CodeArea[] areas3 = validate(fLineNumbersCM.mapToMemory(fProcessId, pcCodeAreas[0].file, pcCodeAreas[0].start_line, pcCodeAreas[0].start_column));
+ Assert.fail("Expected error");
+ } catch (ExecutionException e) {}
+ try {
+ validate(fLineNumbersCM.mapToMemory(fThreadId, pcCodeAreas[0].file, pcCodeAreas[0].start_line, pcCodeAreas[0].start_column));
+ 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");
+ initProcessModel("tcf_test_func2");
// Retrieve the current PC and top frame for use later
final String pc = new Transaction<String>() {
@@ -406,10 +271,10 @@ public class SampleTest extends AbstractTcfUITest
}
public void testRunControlCMChildrenInvalidation() throws Exception {
- createBreakpoint("BP1", "tcf_test_func0");
- startProcess();
- runToTestEntry("tcf_test_func0");
+ initProcessModel("tcf_test_func0");
+ createBreakpoint("testRunControlCMChildrenInvalidation", "tcf_test_func0");
+
// Wait for each threads to start.
final String[] threads = new Transaction<String[]>() {
List<String> fThreads = new ArrayList<String>();
@@ -497,6 +362,9 @@ public class SampleTest extends AbstractTcfUITest
return null;
}
}.get();
+
+ removeBreakpoint("testRunControlCMChildrenInvalidation");
+
}
}
diff --git a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/TransactionTests.java b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/TransactionTests.java
new file mode 100644
index 0000000..f1d0d19
--- /dev/null
+++ b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/TransactionTests.java
@@ -0,0 +1,167 @@
+/*******************************************************************************
+ * Copyright (c) 2006 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.Arrays;
+import java.util.concurrent.ExecutionException;
+
+import junit.framework.Assert;
+import junit.framework.TestCase;
+
+import org.eclipse.tcf.debug.test.util.CallbackCache;
+import org.eclipse.tcf.debug.test.util.DataCallback;
+import org.eclipse.tcf.debug.test.util.Query;
+import org.eclipse.tcf.debug.test.util.Transaction;
+import org.eclipse.tcf.protocol.Protocol;
+
+/**
+ * Tests that exercise the Transaction object.
+ */
+public class TransactionTests extends TestCase {
+ final static private int NUM_CACHES = 5;
+
+ TestCache[] fTestCaches = new TestCache[NUM_CACHES];
+ DataCallback<?>[] fRetrieveRms = new DataCallback<?>[NUM_CACHES];
+
+ class TestCache extends CallbackCache<Integer> {
+
+ final private int fIndex;
+
+ public TestCache(int index) {
+ fIndex = index;
+ }
+
+ @Override
+ protected void retrieve(DataCallback<Integer> rm) {
+ synchronized(TransactionTests.this) {
+ fRetrieveRms[fIndex] = rm;
+ TransactionTests.this.notifyAll();
+ }
+ }
+
+ }
+
+ class TestSingleTransaction extends Transaction<Integer> {
+
+ @Override
+ protected Integer process() throws InvalidCacheException, ExecutionException {
+ validate(fTestCaches[0]);
+ return fTestCaches[0].getData();
+ }
+ }
+
+ class TestSumTransaction extends Transaction<Integer> {
+ @Override
+ protected Integer process() throws InvalidCacheException, ExecutionException {
+ validate(fTestCaches);
+
+ int sum = 0;
+ for (CallbackCache<Integer> cache : fTestCaches) {
+ sum += cache.getData();
+ }
+ return sum;
+ }
+ }
+
+ /**
+ * There's no rule on how quickly the cache has to start data retrieval
+ * after it has been requested. It could do it immediately, or it could
+ * wait a dispatch cycle, etc..
+ */
+ private void waitForRetrieveRm(boolean all) {
+ synchronized(this) {
+ if (all) {
+ while (Arrays.asList(fRetrieveRms).contains(null)) {
+ try {
+ wait();
+ } catch (InterruptedException e) {
+ return;
+ }
+ }
+ } else {
+ while (fRetrieveRms[0] == null) {
+ try {
+ wait();
+ } catch (InterruptedException e) {
+ return;
+ }
+ }
+ }
+ }
+ }
+
+ public void setUp() throws ExecutionException, InterruptedException {
+ for (int i = 0; i < fTestCaches.length; i++) {
+ fTestCaches[i] = new TestCache(i);
+ }
+ }
+
+ public void tearDown() throws ExecutionException, InterruptedException {
+ fRetrieveRms = new DataCallback<?>[NUM_CACHES];
+ fTestCaches = new TestCache[NUM_CACHES];
+ }
+
+ public void testSingleTransaction() throws InterruptedException, ExecutionException {
+ final TestSingleTransaction testTransaction = new TestSingleTransaction();
+ // Request data from cache
+ Query<Integer> q = new Query<Integer>() {
+ @Override
+ protected void execute(DataCallback<Integer> rm) {
+ testTransaction.request(rm);
+ }
+ };
+ q.invoke();
+
+ // Wait until the cache starts data retrieval.
+ waitForRetrieveRm(false);
+
+ // Set the data to caches.
+ Protocol.invokeAndWait(new Runnable() {
+ public void run() {
+ ((DataCallback<Integer>)fRetrieveRms[0]).setData(1);
+ fRetrieveRms[0].done();
+ }
+ });
+
+ Assert.assertEquals(1, (int)q.get());
+ }
+
+ public void testSumTransaction() throws InterruptedException, ExecutionException {
+
+ final TestSumTransaction testTransaction = new TestSumTransaction();
+ // Request data from cache
+ Query<Integer> q = new Query<Integer>() {
+ @Override
+ protected void execute(DataCallback<Integer> rm) {
+ testTransaction.request(rm);
+ }
+ };
+ q.invoke();
+
+ // Wait until the cache starts data retrieval.
+ waitForRetrieveRm(true);
+
+
+ // Set the data to caches.
+ Protocol.invokeAndWait(new Runnable() {
+ public void run() {
+ for (DataCallback<?> rm : fRetrieveRms) {
+ ((DataCallback<Integer>)rm).setData(1);
+ rm.done();
+ }
+ }
+ });
+
+ q.invoke();
+ Assert.assertEquals(NUM_CACHES, (int)q.get());
+ }
+
+}
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 c175167..faeb6f8 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
@@ -20,6 +20,7 @@ import java.util.TreeSet;
import junit.framework.Assert;
import org.eclipse.debug.internal.ui.viewers.model.ILabelUpdateListener;
+import org.eclipse.debug.internal.ui.viewers.model.ITreeModelViewer;
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.IHasChildrenUpdate;
@@ -29,7 +30,6 @@ import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDeltaVisitor;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelProxy;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IStateUpdateListener;
-import org.eclipse.debug.internal.ui.viewers.model.provisional.ITreeModelViewer;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerUpdate;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerUpdateListener;
import org.eclipse.jface.viewers.TreePath;
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
index 3efade5..b4f1798 100644
--- 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
@@ -287,8 +287,8 @@ public class BreakpointsCM extends AbstractCacheManager implements IBreakpoints.
// 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 (entry.getKey() instanceof EventKey) {
+ EventKey<?> eventKey = (EventKey<?>)entry.getKey();
if ( ContextAddedCache.class.equals( eventKey.getCacheClass() ) ) {
((ContextAddedCache)entry.getValue()).eventReceived(bps);
}
@@ -312,8 +312,8 @@ public class BreakpointsCM extends AbstractCacheManager implements IBreakpoints.
// 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 (entry.getKey() instanceof EventKey) {
+ EventKey<?> eventKey = (EventKey<?>)entry.getKey();
if ( ContextChangedCache.class.equals( eventKey.getCacheClass() ) ) {
((ContextChangedCache)entry.getValue()).eventReceived(bps);
}
@@ -342,6 +342,16 @@ public class BreakpointsCM extends AbstractCacheManager implements IBreakpoints.
PropertiesCache cache = mapCache(new PropertiesCacheKey(id));
cache.resetProperties();
}
+
+ // TODO: avoid iterating over all entries, use separate list for events.
+ for (Map.Entry<Key<?>, ICache<?>> entry: fMap.entrySet()) {
+ if (entry.getKey() instanceof EventKey) {
+ EventKey<?> eventKey = (EventKey<?>)entry.getKey();
+ if ( ContextRemovedCache.class.equals( eventKey.getCacheClass() ) ) {
+ ((ContextRemovedCache)entry.getValue()).eventReceived(ids);
+ }
+ }
+ }
}
diff --git a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/LineNumbersCM.java b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/LineNumbersCM.java
new file mode 100644
index 0000000..6395543
--- /dev/null
+++ b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/LineNumbersCM.java
@@ -0,0 +1,232 @@
+/*******************************************************************************sbsb
+ * 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.Map;
+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.debug.test.util.TransactionCache;
+import org.eclipse.tcf.protocol.IToken;
+import org.eclipse.tcf.services.ILineNumbers;
+import org.eclipse.tcf.services.ILineNumbers.CodeArea;
+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;
+
+/**
+ *
+ */
+public class LineNumbersCM extends AbstractCacheManager {
+
+ private ResetMap fMemContextResetMap = new ResetMap();
+ private ILineNumbers fService;
+ private IMemoryMap fMemoryMap;
+ private RunControlCM fRunControlCM;
+
+ public LineNumbersCM(ILineNumbers lineNumbers, IMemoryMap memMap, RunControlCM runControlCM) {
+ fService = lineNumbers;
+ fMemoryMap = memMap;
+ fMemoryMap.addListener(fMemoryMapListener);
+ fRunControlCM = runControlCM;
+ fRunControlCM.addListener(fRunControlListener);
+ }
+
+ @Override
+ public void dispose() {
+ fRunControlCM.removeListener(fRunControlListener);
+ fMemoryMap.removeListener(fMemoryMapListener);
+ super.dispose();
+ }
+
+ private abstract class LineNumbersTokenCache<V> extends TokenCache<V> {
+ abstract protected String getId();
+
+ protected void set(IToken token, V data, Throwable error) {
+ fMemContextResetMap.addValid(getId(), this);
+ super.set(token, data, error);
+ }
+ }
+
+ abstract private class MapToSourceKey<V> extends IdKey<V> {
+ private final Number fStartAdddress;
+ private final Number fEndAddress;
+
+ public MapToSourceKey(Class<V> cacheClass, String id, Number startAddress, Number endAddress) {
+ super(cacheClass, id);
+ fStartAdddress = startAddress;
+ fEndAddress = endAddress;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (super.equals(obj) && obj instanceof MapToSourceKey<?>) {
+ MapToSourceKey<?> other = (MapToSourceKey<?>)obj;
+ return fStartAdddress.equals(other.fStartAdddress) && fEndAddress.equals(other.fEndAddress);
+ }
+ return false;
+ }
+ @Override
+ public int hashCode() {
+ return super.hashCode() + fStartAdddress.hashCode() + fEndAddress.hashCode();
+ }
+ }
+
+ public ICache<CodeArea[]> mapToSource(final String context_id, final Number start_address, final Number end_address) {
+ class MyCache extends TransactionCache<ILineNumbers.CodeArea[]> {
+ private String fId = context_id;
+
+ @Override
+ protected CodeArea[] process() throws InvalidCacheException, ExecutionException {
+ RunControlContext rcContext = validate(fRunControlCM.getContext(fId));
+ String mem_id = rcContext.getProcessID();
+ if (mem_id == null) {
+ // TODO: is this the correct fall-back. Should we save the parent ID for reset?
+ mem_id = fId;
+ }
+ return validate( doMapToSource(mem_id, start_address, end_address) );
+ }
+ }
+
+ return mapCache(new MapToSourceKey<MyCache>(MyCache.class, context_id, start_address, end_address) {
+ @Override MyCache createCache() { return new MyCache(); }
+ });
+ }
+
+ private ICache<CodeArea[]> doMapToSource(final String mem_id, final Number start_address, final Number end_address) {
+ class MyCache extends LineNumbersTokenCache<CodeArea[]> implements ILineNumbers.DoneMapToSource {
+ @Override
+ protected String getId() {
+ return mem_id;
+ }
+ @Override
+ protected IToken retrieveToken() {
+ return fService.mapToSource(mem_id, start_address, end_address, this);
+ }
+
+ public void doneMapToSource(IToken token, Exception error, CodeArea[] areas) {
+ set(token, areas, error);
+ }
+
+ };
+
+ return mapCache(new MapToSourceKey<MyCache>(MyCache.class, mem_id, start_address, end_address) {
+ @Override MyCache createCache() { return new MyCache(); }
+ });
+
+ }
+
+ abstract private class MapToMemoryKey<V> extends IdKey<V> {
+ private final String fFile;
+ private final int fLine;
+ private final int fColumn;
+
+ public MapToMemoryKey(Class<V> cacheClass, String id, String file, int line, int col) {
+ super(cacheClass, id);
+ fFile = file;
+ fLine = line;
+ fColumn = col;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (super.equals(obj) && obj instanceof MapToMemoryKey<?>) {
+ MapToMemoryKey<?> other = (MapToMemoryKey<?>)obj;
+ return fFile.equals(other.fFile) && fLine == other.fLine && fColumn == other.fColumn;
+ }
+ return false;
+ }
+ @Override
+ public int hashCode() {
+ return super.hashCode() + fFile.hashCode()^fLine^(fColumn + 1);
+ }
+ }
+
+ public ICache<CodeArea[]> mapToMemory(final String context_id, final String file, final int line, final int column) {
+ class MyCache extends TransactionCache<ILineNumbers.CodeArea[]> {
+ private String fId = context_id;
+
+ protected CodeArea[] process() throws InvalidCacheException, ExecutionException {
+ RunControlContext rcContext = validate(fRunControlCM.getContext(fId));
+ String mem_id = rcContext.getProcessID();
+ if (mem_id == null) {
+ // TODO: is this the correct fall-back. Should we save the parent ID for reset?
+ mem_id = fId;
+ }
+ return validate( doMapToMemory(mem_id, file, line, column) );
+ }
+ }
+
+ return mapCache(new MapToMemoryKey<MyCache>(MyCache.class, context_id, file, line, column) {
+ @Override MyCache createCache() { return new MyCache(); }
+ });
+ }
+
+ private ICache<CodeArea[]> doMapToMemory(final String mem_id, final String file, final int line, final int column) {
+ class MyCache extends LineNumbersTokenCache<CodeArea[]> implements ILineNumbers.DoneMapToMemory {
+ @Override
+ protected String getId() {
+ return mem_id;
+ }
+ @Override
+ protected IToken retrieveToken() {
+ return fService.mapToMemory(mem_id, file, line, column, this);
+ }
+ public void doneMapToMemory(IToken token, Exception error, CodeArea[] areas) {
+ set(token, areas, error);
+ }
+
+ };
+
+ return mapCache(new MapToMemoryKey<MyCache>(MyCache.class, mem_id, file, line, column) {
+ @Override MyCache createCache() { return new MyCache(); }
+ });
+
+ }
+
+ interface DoneMapToMemory {
+ void doneMapToMemory(IToken token, Exception error, CodeArea[] areas);
+ }
+
+ private RunControlListener fRunControlListener = new RunControlListener() {
+
+ public void contextRemoved(String[] context_ids) {
+ for (String id : context_ids) {
+ resetContext(id);
+ }
+ }
+
+ public void contextAdded(RunControlContext[] contexts) {}
+ public void contextChanged(RunControlContext[] contexts) {}
+ public void contextSuspended(String context, String pc, String reason, Map<String, Object> params) {}
+ public void contextResumed(String context) {}
+ public void containerSuspended(String context, String pc, String reason, Map<String, Object> params,
+ String[] suspended_ids) {}
+ public void containerResumed(String[] context_ids) {}
+ public void contextException(String context, String msg) {}
+ };
+
+ private void resetContext(String id) {
+ fMemContextResetMap.reset(id);
+ }
+
+ private MemoryMapListener fMemoryMapListener = new MemoryMapListener() {
+ public void changed(String context_id) {
+ resetContext(context_id);
+ }
+ };
+
+}
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
index 29dbd81..fcb7372 100644
--- 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
@@ -90,14 +90,14 @@ public abstract class AbstractCache<V> implements ICache<V> {
}
public V getData() {
- if (!fValid) {
+ if (!isValid()) {
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) {
+ if (!isValid()) {
throw new IllegalStateException("Cache is not valid. Cache status can be read only when cache is valid."); //$NON-NLS-1$
}
return fError;
@@ -106,42 +106,37 @@ public abstract class AbstractCache<V> implements ICache<V> {
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;
+ 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;
}
- } else {
- Callback[] newWaitingList = new Callback[2];
- newWaitingList[0] = (Callback)fWaitingList;
- newWaitingList[1] = rm;
+ }
+ if (i == waitingListLength) {
+ Callback[] newWaitingList = new Callback[waitingListLength + 1];
+ System.arraycopy(waitingList, 0, newWaitingList, 0, waitingListLength);
+ newWaitingList[waitingListLength] = rm;
fWaitingList = newWaitingList;
}
- }
- rm.addCancelListener(fRequestCanceledListener);
- if (first) {
- retrieve();
+ } else {
+ Callback[] newWaitingList = new Callback[2];
+ newWaitingList[0] = (Callback)fWaitingList;
+ newWaitingList[1] = rm;
+ fWaitingList = newWaitingList;
}
- } else {
- rm.setError(fError);
- rm.done();
+ }
+ rm.addCancelListener(fRequestCanceledListener);
+ if (first && !isValid()) {
+ retrieve();
}
}
@@ -167,7 +162,12 @@ public abstract class AbstractCache<V> implements ICache<V> {
}
private void completeWaitingRm(Callback rm) {
- rm.setError(fError);
+ if (!isValid() && fError == null) {
+ rm.setError(INVALID_STATUS);
+ } else {
+ rm.setError(fError);
+ }
+
rm.removeCancelListener(fRequestCanceledListener);
rm.done();
}
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
index eca1bb7..c4c761a 100644
--- 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
@@ -39,15 +39,19 @@ public interface ICache<V> {
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()}
+ * Asks the cache to update its value 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.
+ * <p>If the cache is already valid, the cache is not updated again from
+ * source. Instead the callback is completed next time the cache state is
+ * changed. Clients can use this feature to be notified when the cache is
+ * being reset.
+ * </p>
+ * @param cb
+ * Callback that is called when cache becomes valid.
*/
- public void update(Callback rm);
+ public void update(Callback cb);
/**
* Returns <code>true</code> if the cache is currently valid. I.e.
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
index d27281e..7729ad1 100644
--- 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
@@ -17,6 +17,7 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.eclipse.core.runtime.CoreException;
+import org.eclipse.tcf.protocol.Protocol;
/**
* @since 2.2
@@ -62,6 +63,36 @@ public abstract class Transaction<V> implements Future<V> {
execute();
}
+ protected void preProcess() {}
+
+ protected void postProcess(boolean done, V data, Throwable error) {}
+
+ protected boolean processUnchecked() {
+ 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
+ setData(data);
+ return true;
+ }
+ 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.
+ return false;
+ }
+ catch (Throwable e) {
+ // At least one of the cache objects encountered a failure obtaining
+ // the data from the source. Complete the request.
+ setError(e);
+ return true;
+ }
+ }
+
/**
* The transaction logic--code that tries to synchronously make use of,
* usually, multiple data points that are normally obtained asynchronously.
@@ -83,6 +114,24 @@ public abstract class Transaction<V> implements Future<V> {
*/
abstract protected V process() throws InvalidCacheException, ExecutionException;
+ /**
+ * Can be called only while in process().
+ * @param data
+ */
+ protected void setData(V data) {
+ assert Protocol.isDispatchThread();
+ fRm.setData(data);
+ }
+
+ /**
+ * Can be called only while in process().
+ * @param data
+ */
+ protected void setError(Throwable error) {
+ assert Protocol.isDispatchThread();
+ fRm.setError(error);
+ }
+
/**
* Method which invokes the transaction logic and handles any exception that
* may result. If that logic encounters a stale/unset cache object, then we
@@ -95,32 +144,15 @@ public abstract class Transaction<V> implements Future<V> {
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);
+
+ preProcess();
+ if (processUnchecked()) {
+ postProcess(true, fRm.getData(), fRm.getError());
fRm.done();
fRm = null;
+ } else {
+ postProcess(false, null, null);
}
-
}
/**
diff --git a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/TransactionCache.java b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/TransactionCache.java
new file mode 100644
index 0000000..8749453
--- /dev/null
+++ b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/TransactionCache.java
@@ -0,0 +1,111 @@
+/*******************************************************************************
+ * 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.util;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+
+
+/**
+ *
+ */
+public abstract class TransactionCache<V> extends Transaction<V> implements ICache<V> {
+
+ private List<ICache<?>> fDependsOn;
+ private List<Callback> fDependsOnCallbacks;
+
+ private CallbackCache<V> fCache = new CallbackCache<V>() {
+ @Override
+ protected void retrieve(DataCallback<V> rm) {
+ request(rm);
+ }
+ };
+
+ public V getData() {
+ return fCache.getData();
+ }
+
+ public Throwable getError() {
+ return fCache.getError();
+ }
+
+ public void update(Callback rm) {
+ fCache.update(rm);
+ }
+
+ public boolean isValid() {
+ return fCache.isValid();
+ }
+
+ @Override
+ protected void preProcess() {
+ super.preProcess();
+
+ if (fDependsOnCallbacks != null) {
+ for (Callback cb : fDependsOnCallbacks) {
+ cb.cancel();
+ }
+ fDependsOnCallbacks = null;
+ }
+ fDependsOn = new ArrayList<ICache<?>>(4);
+ }
+
+ protected void postProcess(boolean done, V data, Throwable error) {
+ if (done) {
+ fDependsOnCallbacks = new ArrayList<Callback>(fDependsOn.size());
+ for (ICache<?> cache : fDependsOn) {
+ assert cache.isValid();
+ cache.update(new Callback() {
+ @Override
+ protected void handleCompleted() {
+ if (!isCancelled()) {
+ fCache.reset();
+ for (Callback cb : fDependsOnCallbacks) {
+ if (cb == this) continue;
+ cb.cancel();
+ }
+ }
+ }
+ });
+ }
+ } else {
+ fDependsOn = null;
+ }
+ super.postProcess(done, data, error);
+ }
+
+ /**
+ * Can be called while in {@link #process()}
+ * @param cache
+ */
+ public void addDependsOn(ICache cache) {
+ fDependsOn.add(cache);
+ }
+
+ public <T> T validate(ICache<T> cache) throws InvalidCacheException, ExecutionException {
+ if (cache.isValid()) {
+ addDependsOn(cache);
+ }
+ return super.validate(cache);
+ }
+
+ @Override
+ public void validate(Iterable caches) throws InvalidCacheException, ExecutionException {
+ for (Object cacheObj : caches) {
+ ICache<?> cache = (ICache<?>)cacheObj;
+ if (cache.isValid()) {
+ addDependsOn(cache);
+ }
+ }
+ super.validate(caches);
+ }
+}