| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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(); | ||
| } | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,115 +1,101 @@ | ||
| package org.eclipse.tcf.debug.test; | ||
|
|
||
| import java.math.BigInteger; | ||
| import java.util.Map; | ||
| 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.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; | ||
|
|
||
| public class BreakpointsTest extends AbstractTcfUITest | ||
| { | ||
| 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); | ||
| } | ||
|
|
||
| @Override | ||
| protected void tearDown() throws Exception { | ||
| fBpListener.dispose(); | ||
| super.tearDown(); | ||
| } | ||
| private CodeArea getFunctionCodeArea(String functionName) throws Exception { | ||
| return new Transaction<CodeArea>() { | ||
| @Override | ||
| 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 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); | ||
| }}); | ||
|
|
||
| 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)); | ||
|
|
||
| return bp; | ||
| } | ||
|
|
||
| public void testContextAddedOnLineBrakpointCreate() throws Exception { | ||
| initProcessModel("tcf_test_func0"); | ||
|
|
||
| CodeArea bpCodeArea = getFunctionCodeArea("tcf_test_func0"); | ||
| ICLineBreakpoint bp = createLineBreakpoint(bpCodeArea.file, bpCodeArea.start_line); | ||
| } | ||
|
|
||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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$ | ||
| } | ||
|
|
||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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()); | ||
| } | ||
|
|
||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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); | ||
| } | ||
| }; | ||
|
|
||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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); | ||
| } | ||
| } |