/******************************************************************************* * Copyright (c) 2007, 2010 Ericsson 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: * Ericsson - Initial Implementation *******************************************************************************/ package org.eclipse.cdt.tests.dsf.gdb.tests; import static org.junit.Assert.assertTrue; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import org.eclipse.cdt.core.IAddress; import org.eclipse.cdt.debug.core.ICDTLaunchConfigurationConstants; import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; import org.eclipse.cdt.dsf.concurrent.RequestMonitor; import org.eclipse.cdt.dsf.datamodel.IDMContext; import org.eclipse.cdt.dsf.debug.service.IExpressions; import org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionChangedDMEvent; import org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionDMAddress; import org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionDMContext; import org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionDMData; import org.eclipse.cdt.dsf.debug.service.IExpressions3.IExpressionDMDataExtension; import org.eclipse.cdt.dsf.debug.service.IFormattedValues; import org.eclipse.cdt.dsf.debug.service.IFormattedValues.FormattedValueDMContext; import org.eclipse.cdt.dsf.debug.service.IFormattedValues.FormattedValueDMData; import org.eclipse.cdt.dsf.debug.service.IRunControl.StepType; import org.eclipse.cdt.dsf.debug.service.IStack.IFrameDMContext; import org.eclipse.cdt.dsf.mi.service.ClassAccessor.MIExpressionDMCAccessor; import org.eclipse.cdt.dsf.mi.service.MIExpressions; import org.eclipse.cdt.dsf.mi.service.command.events.MIStoppedEvent; import org.eclipse.cdt.dsf.service.DsfServiceEventHandler; import org.eclipse.cdt.dsf.service.DsfServicesTracker; import org.eclipse.cdt.dsf.service.DsfSession; import org.eclipse.cdt.tests.dsf.gdb.framework.AsyncCompletionWaitor; import org.eclipse.cdt.tests.dsf.gdb.framework.BackgroundRunner; import org.eclipse.cdt.tests.dsf.gdb.framework.BaseTestCase; import org.eclipse.cdt.tests.dsf.gdb.framework.SyncUtil; import org.eclipse.cdt.tests.dsf.gdb.launching.TestsPlugin; import org.eclipse.cdt.utils.Addr32; import org.eclipse.cdt.utils.Addr64; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.junit.After; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; @RunWith(BackgroundRunner.class) public class MIExpressionsTest extends BaseTestCase { private DsfSession fSession; private DsfServicesTracker fServicesTracker; protected IExpressions fExpService; private int fExprChangedEventCount = 0; private IExpressionDMContext fExprChangedCtx = null; private IExpressionDMContext globalExpressionCtx1 = null; private IExpressionDMContext globalExpressionCtx2 = null; @BeforeClass public static void beforeClassMethod() { setLaunchAttribute(ICDTLaunchConfigurationConstants.ATTR_PROGRAM_NAME, "data/launch/bin/ExpressionTestApp.exe"); } @Before public void init() throws Exception { fSession = getGDBLaunch().getSession(); Runnable runnable = new Runnable() { public void run() { fServicesTracker = new DsfServicesTracker(TestsPlugin.getBundleContext(), fSession.getId()); fExpService = fServicesTracker.getService(IExpressions.class); fSession.addServiceEventListener(MIExpressionsTest.this, null); clearExprChangedData(); } }; fSession.getExecutor().submit(runnable).get(); } @After public void shutdown() throws Exception { Runnable runnable = new Runnable() { public void run() { fSession.removeServiceEventListener(MIExpressionsTest.this); } }; fSession.getExecutor().submit(runnable).get(); fExpService = null; fServicesTracker.dispose(); } // Handles ExpressionChangedEvent @DsfServiceEventHandler public void eventDispatched(IExpressionChangedDMEvent e) { fExprChangedEventCount++; fExprChangedCtx = e.getDMContext(); } // Clears the counters private void clearExprChangedData() { fExprChangedEventCount = 0; fExprChangedCtx = null; } // Returns the total number of events received private int getExprChangedCount() { return fExprChangedEventCount; } private IExpressionDMContext getExprChangedContext() { return fExprChangedCtx; } // ********************************************************************* // Below are the tests for the expression service. // ********************************************************************* /** * Test that we can correctly evaluate integer expressions. */ @Test public void testLiteralIntegerExpressions() throws Throwable { MIStoppedEvent stoppedEvent = SyncUtil.runToLocation("testLocals"); // Create a map of expressions and their expected values. Map<String, String[]> tests = new HashMap<String, String[]>(); tests.put("0 + 0 - 0", new String[] { "0x0", "0", "0", "0", "0", "0" }); tests.put("3 + 4", new String[] { "0x7", "07", "111", "7", "7", "7" }); tests.put("3 + 4 * 5", new String[] { "0x17", "027", "10111", "23", "23", "23" }); tests.put("5 * 3 + 4", new String[] { "0x13", "023", "10011", "19", "19", "19" }); tests.put("5 * (3 + 4)", new String[] { "0x23", "043", "100011", "35", "35", "35" }); tests.put("10 - 15", new String[] { "0xFFFFFFFB", "037777777773", "11111111111111111111111111111011", "-5", "-5", "-5" }); tests.put("10 + -15", new String[] { "0xFFFFFFFB", "037777777773", "11111111111111111111111111111011", "-5", "-5", "-5" }); executeExpressionSubTests(tests, SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0)); } /** * Test that we can correctly evaluate floating-point expressions. */ @Test public void testLiteralFloatingPointExpressions() throws Throwable { MIStoppedEvent stoppedEvent = SyncUtil.runToLocation("testLocals"); // Create a map of expressions and their expected values. Map<String, String[]> tests = new HashMap<String, String[]>(); tests.put("3.14159 + 1.1111", new String[] { "0x4", "04", "100", "4", "4.2526", "4.2526" }); tests.put("100.0 / 3.0", new String[] { "0x21", "041", "100001", "33", "33.3333", "33.3333" }); tests.put("-100.0 / 3.0", new String[] { "0xffffffffffffffdf", "01777777777777777777737", "1111111111111111111111111111111111111111111111111111111111011111", "-33", "-33.3333", "-33.3333" }); tests.put("-100.0 / -3.0", new String[] { "0x21", "041", "100001", "33", "33.3333", "33.3333" }); executeExpressionSubTests(tests, false, SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0)); tests.clear(); tests.put("100.0 / 0.5", new String[] { "0xc8", "0310", "11001000", "200", "200", "200" }); executeExpressionSubTests(tests, true, SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0)); } /** * Test that we can correctly evaluate C expressions involving local * variables. */ @Test public void testLocalVariables() throws Throwable { // Run to the point where all local variables are initialized SyncUtil.runToLocation("testLocals"); MIStoppedEvent stoppedEvent = SyncUtil.step(16, StepType.STEP_OVER); // Create a map of expressions to expected values. Map<String, String[]> tests1 = new HashMap<String, String[]>(); tests1.put("lIntVar", new String[] { "0x3039", "030071", "11000000111001", "12345", "12345", "12345" }); tests1.put("lDoubleVar", new String[] { "0x3039", "030071", "11000000111001", "12345", "12345.123449999999", "12345.123449999999" }); tests1.put("lCharVar", new String[] { "0x6d", "0155", "1101101", "109", "109 'm'", "109 'm'" }); tests1.put("lBoolVar", new String[] { "0x0", "0", "0", "0", "false", "false" }); tests1.put("lIntArray[1]", new String[] { "0x3039", "030071", "11000000111001", "12345", "12345", "12345" }); tests1.put("lDoubleArray[1]", new String[] { "0x3039", "030071", "11000000111001", "12345", "12345.123449999999", "12345.123449999999" }); tests1.put("lCharArray[1]", new String[] { "0x6d", "0155", "1101101", "109", "109 'm'", "109 'm'" }); tests1.put("lBoolArray[1]", new String[] { "0x0", "0", "0", "0", "false", "false" }); tests1.put("*lIntPtr", new String[] { "0x3039", "030071", "11000000111001", "12345", "12345", "12345" }); tests1.put("*lDoublePtr", new String[] { "0x3039", "030071", "11000000111001", "12345", "12345.123449999999", "12345.123449999999" }); tests1.put("*lCharPtr", new String[] { "0x6d", "0155", "1101101", "109", "109 'm'", "109 'm'" }); tests1.put("*lBoolPtr", new String[] { "0x0", "0", "0", "0", "false", "false" }); tests1.put("lIntPtr2", new String[] { "0x1", "01", "1", "1", "0x1", "0x1" }); tests1.put("lDoublePtr2", new String[] { "0x2345", "021505", "10001101000101", "9029", "0x2345", "0x2345" }); // GDB says a char* is out of bounds, but not the other pointers??? // tests1.put("CharPtr2", new String[] { "0x1234", "011064", // "1001000110100", "4660", "0x1234" }); tests1.put("lBoolPtr2", new String[] { "0x123ABCDE", "02216536336", "10010001110101011110011011110", "305839326", "0x123ABCDE", "0x123ABCDE" }); executeExpressionSubTests(tests1, SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0)); // Step into the method and stop until all new local variables are // initialized SyncUtil.step(StepType.STEP_INTO); stoppedEvent = SyncUtil.step(5, StepType.STEP_OVER); // Create a map of expressions to expected values. Map<String, String[]> tests2 = new HashMap<String, String[]>(); tests2.put("lIntVar", new String[] { "0x1a85", "015205", "1101010000101", "6789", "6789", "6789" }); tests2.put("lDoubleArray[1]", new String[] { "0x1a85", "015205", "1101010000101", "6789", "6789.6788999999999", "6789.6788999999999" }); tests2.put("lCharVar", new String[] { "0x69", "0151", "1101001", "105", "105 'i'", "105 'i'" }); tests2.put("*lCharPtr", new String[] { "0x69", "0151", "1101001", "105", "105 'i'", "105 'i'" }); tests2.put("lBoolPtr2", new String[] { "0xABCDE123", "025363360443", "10101011110011011110000100100011", "2882396451", "0xABCDE123","0xABCDE123" }); // check variables at current stack frame executeExpressionSubTests(tests2, SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0)); // check previous stack frame executeExpressionSubTests(tests1, SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 1)); // Now return from the method and check that we see the // original variables. We must use the right context to restore the right stack frame stoppedEvent = SyncUtil.step(stoppedEvent.getDMContext(), StepType.STEP_RETURN); executeExpressionSubTests(tests1, SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0)); } /** * This tests verifies that we can deal with variables in a subblock hiding * variables with the same name in the outer block. */ @Ignore("Sublocks do not work with GDB") @Test public void testSubBlock() throws Throwable { SyncUtil.runToLocation("testSubblock"); MIStoppedEvent stoppedEvent = SyncUtil.step(2, StepType.STEP_OVER); IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); Map<String, String[]> tests = new HashMap<String, String[]>(); tests.put("a", new String[] { "0x8", "010", "1000", "8", "8", "8" }); tests.put("b", new String[] { "0x1", "01", "1", "1", "1", "1" }); executeExpressionSubTests(tests, frameDmc); // Now enter a subblock with the same variable names SyncUtil.step(2, StepType.STEP_OVER); tests = new HashMap<String, String[]>(); tests.put("a", new String[] { "0xc", "014", "1100", "12", "12", "12" }); tests.put("b", new String[] { "0x1", "01", "1", "1", "1", "1" }); executeExpressionSubTests(tests, frameDmc); // Now step to change the b variable SyncUtil.step(1, StepType.STEP_OVER); tests = new HashMap<String, String[]>(); tests.put("a", new String[] { "0xc", "014", "1100", "12", "12", "12" }); tests.put("b", new String[] { "0xc", "014", "1100", "12", "12", "12" }); executeExpressionSubTests(tests, frameDmc); // Now exit the sub-block and check that we see the original a but the // same b SyncUtil.step(1, StepType.STEP_OVER); tests = new HashMap<String, String[]>(); tests.put("a", new String[] { "0x8", "010", "1000", "8", "8", "8" }); tests.put("b", new String[] { "0xc", "014", "1100", "12", "12", "12" }); executeExpressionSubTests(tests, frameDmc); } /** * This tests verifies that we can obtain children properly. */ @Test public void testChildren() throws Throwable { // Get the children of some variables MIStoppedEvent stoppedEvent = SyncUtil.runToLocation("testChildren"); doTestChildren(stoppedEvent); // Now do a step and get the children again, to test the internal cache stoppedEvent = SyncUtil.step(1, StepType.STEP_OVER); doTestChildren(stoppedEvent); } /** * This test makes sure we get the right number of children. */ @Test public void testChildrenCount() throws Throwable { // Next we test that we can retrieve children count while reading the // value and vice-versa SyncUtil.runToLocation("testChildren"); MIStoppedEvent stoppedEvent = SyncUtil.step(1, StepType.STEP_OVER); final IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); final AsyncCompletionWaitor wait = new AsyncCompletionWaitor(); // First we get the expected value of the array pointer. final IExpressionDMContext exprDmc = SyncUtil.createExpression(frameDmc, "f"); fExpService.getExecutor().submit(new Runnable() { public void run() { fExpService.getSubExpressionCount( exprDmc, new DataRequestMonitor<Integer>(fExpService.getExecutor(), null) { @Override protected void handleCompleted() { if (!isSuccess()) { wait.waitFinished(getStatus()); } else { int count = getData(); if (count != 5) { wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, "Failed getting count for children. Got " + count + " instead of 5", null)); } else { wait.waitFinished(); } } } }); } }); wait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); assertTrue(wait.getMessage(), wait.isOK()); } /** * This test makes sure we get can tell if an expression has children * based on the expression data. */ @Test public void testHasChildrenInExpressionData() throws Throwable { SyncUtil.runToLocation("testChildren"); MIStoppedEvent stoppedEvent = SyncUtil.step(1, StepType.STEP_OVER); final IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); final AsyncCompletionWaitor wait = new AsyncCompletionWaitor(); // First we get the expected value of the array pointer. final IExpressionDMContext exprDmc = SyncUtil.createExpression(frameDmc, "f"); fExpService.getExecutor().submit(new Runnable() { public void run() { fExpService.getExpressionData( exprDmc, new DataRequestMonitor<IExpressionDMData>(fExpService.getExecutor(), null) { @Override protected void handleCompleted() { if (!isSuccess()) { wait.waitFinished(getStatus()); } else { if ((getData() instanceof IExpressionDMDataExtension) == false) { wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, "Did not a receive an IExpressionDMDataExtension", null)); return; } IExpressionDMDataExtension data = (IExpressionDMDataExtension)getData(); boolean hasChildren = data.hasChildren(); if (!hasChildren) { wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, "No children were found, when we expected to find some", null)); } else { wait.waitFinished(); } } } }); } }); wait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); assertTrue(wait.getMessage(), wait.isOK()); } /** * This test makes sure we properly deal with a GDB display bug. * See bug 320277 * * The following code causes a bug in GDB: * * class Base {}; * class BaseTest: public Base { * public: * BaseTest() {} // Removing this lines removes GDB's bug * void test() { return; } * }; * * We see the bug with the following commands: * -var-create - * this * -var-list-children var1 * -var-info-path-expression var1.BaseTest * -data-evaluate-expression "(*(Base*) this)" * * which we can reproduce by creating the children of this * and asking for the DETAILS_FORMAT of the var1.BaseTest child. */ @Test public void testBaseChildrenBug() throws Throwable { MIStoppedEvent stoppedEvent = SyncUtil.runToLocation("BaseTest::test"); final IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); final AsyncCompletionWaitor wait = new AsyncCompletionWaitor(); // First we get 'this' and its children final IExpressionDMContext exprDmc = SyncUtil.createExpression(frameDmc, "this"); final IExpressionDMContext[] children = getChildren(exprDmc, new String[] {"Base", "Base"}); fExpService.getExecutor().submit(new Runnable() { public void run() { fExpService.getFormattedExpressionValue( fExpService.getFormattedValueContext(children[0], MIExpressions.DETAILS_FORMAT), new DataRequestMonitor<FormattedValueDMData>(fExpService.getExecutor(), null) { @Override protected void handleCompleted() { wait.waitFinished(getStatus()); } }); } }); wait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); assertTrue(wait.getMessage(), wait.isOK()); wait.waitReset(); // This second child is testing the fact that we could have the child named // the same as its type and we still want to be able to get the details without error. fExpService.getExecutor().submit(new Runnable() { public void run() { fExpService.getFormattedExpressionValue( fExpService.getFormattedValueContext(children[1], MIExpressions.DETAILS_FORMAT), new DataRequestMonitor<FormattedValueDMData>(fExpService.getExecutor(), null) { @Override protected void handleCompleted() { wait.waitFinished(getStatus()); } }); } }); wait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); assertTrue(wait.getMessage(), wait.isOK()); } /** * This test makes sure we properly deal with a GDB display bug * and nested children. * See bug 320277. */ @Test public void testNestedBaseChildrenBug() throws Throwable { MIStoppedEvent stoppedEvent = SyncUtil.runToLocation("BaseTest::test"); final IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); final AsyncCompletionWaitor wait = new AsyncCompletionWaitor(); // First we get 'this' and its children final IExpressionDMContext exprDmc = SyncUtil.createExpression(frameDmc, "this"); IExpressionDMContext[] children1 = getChildren(exprDmc, new String[] {"Base", "Base"}); final IExpressionDMContext[] children = getChildren(children1[0], new String[] {"nested", "pNested"}); final IExpressionDMContext[] childOfPointer = getChildren(children[1], new String[] {"*pNested"}); fExpService.getExecutor().submit(new Runnable() { public void run() { fExpService.getFormattedExpressionValue( fExpService.getFormattedValueContext(children[0], MIExpressions.DETAILS_FORMAT), new DataRequestMonitor<FormattedValueDMData>(fExpService.getExecutor(), null) { @Override protected void handleCompleted() { wait.waitFinished(getStatus()); } }); } }); wait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); assertTrue(wait.getMessage(), wait.isOK()); wait.waitReset(); fExpService.getExecutor().submit(new Runnable() { public void run() { fExpService.getFormattedExpressionValue( fExpService.getFormattedValueContext(children[1], MIExpressions.DETAILS_FORMAT), new DataRequestMonitor<FormattedValueDMData>(fExpService.getExecutor(), null) { @Override protected void handleCompleted() { wait.waitFinished(getStatus()); } }); } }); wait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); assertTrue(wait.getMessage(), wait.isOK()); wait.waitReset(); fExpService.getExecutor().submit(new Runnable() { public void run() { fExpService.getFormattedExpressionValue( fExpService.getFormattedValueContext(childOfPointer[0], MIExpressions.DETAILS_FORMAT), new DataRequestMonitor<FormattedValueDMData>(fExpService.getExecutor(), null) { @Override protected void handleCompleted() { wait.waitFinished(getStatus()); } }); } }); wait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); assertTrue(wait.getMessage(), wait.isOK()); } /** * This test verifies that the ExpressionService can write to a variable. */ @Test public void testWriteVariable() throws Throwable { SyncUtil.runToLocation("testWrite"); MIStoppedEvent stoppedEvent = SyncUtil.step(1, StepType.STEP_OVER); final IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); final IExpressionDMContext exprDmc = SyncUtil.createExpression(frameDmc, "a[1]"); writeAndCheck(exprDmc, "987", IFormattedValues.DECIMAL_FORMAT, "987"); writeAndCheck(exprDmc, "16", IFormattedValues.HEX_FORMAT, "22"); writeAndCheck(exprDmc, "0x2e", IFormattedValues.HEX_FORMAT, "46"); writeAndCheck(exprDmc, "16", IFormattedValues.OCTAL_FORMAT, "14"); writeAndCheck(exprDmc, "022", IFormattedValues.OCTAL_FORMAT, "18"); writeAndCheck(exprDmc, "1011", IFormattedValues.BINARY_FORMAT, "11"); writeAndCheck(exprDmc, "0b1001", IFormattedValues.BINARY_FORMAT, "9"); writeAndCheck(exprDmc, "456", IFormattedValues.NATURAL_FORMAT, "456"); } /* * This method does a write and then a read to make sure the new value was * properly written. */ private void writeAndCheck(final IExpressionDMContext exprDmc, final String newValueFormatted, final String format, final String newValueInDecimal) throws Throwable { final AsyncCompletionWaitor wait = new AsyncCompletionWaitor(); // Write the new value using its formatted value fExpService.getExecutor().submit(new Runnable() { public void run() { fExpService.writeExpression( exprDmc, newValueFormatted, format, new RequestMonitor(fExpService.getExecutor(), null) { @Override protected void handleCompleted() { wait.waitFinished(getStatus()); } }); } }); wait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); assertTrue(wait.getMessage(), wait.isOK()); assertTrue("ExprChangedEvent problem: expected 1, received " + getExprChangedCount(), getExprChangedCount() == 1); clearExprChangedData(); wait.waitReset(); // Read the new value in decimal and check that it is what we expected fExpService.getExecutor().submit(new Runnable() { public void run() { fExpService.getFormattedExpressionValue(fExpService.getFormattedValueContext(exprDmc, IFormattedValues.DECIMAL_FORMAT), new DataRequestMonitor<FormattedValueDMData>(fExpService .getExecutor(), null) { @Override protected void handleCompleted() { if (isSuccess()) { wait.setReturnInfo(getData()); } wait.waitFinished(getStatus()); } }); } }); wait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); assertTrue(wait.getMessage(), wait.isOK()); String actualDecimalValue = ((FormattedValueDMData) wait.getReturnInfo()).getFormattedValue(); assertTrue("Failed to correctly evaluate '" + exprDmc.getExpression() + "': expected '" + newValueInDecimal + "', got '" + actualDecimalValue + "'", actualDecimalValue.equalsIgnoreCase(newValueInDecimal)); assertTrue("ExprChangedEvent problem: expected 0, received " + getExprChangedCount(), getExprChangedCount() == 0); } /** * This tests verifies that we handle invalid formats properly for a write. */ @Test public void testWriteErrorFormat() throws Throwable { SyncUtil.runToLocation("testWrite"); MIStoppedEvent stoppedEvent = SyncUtil.step(1, StepType.STEP_OVER); IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); IExpressionDMContext exprDmc = SyncUtil.createExpression(frameDmc, "a[1]"); writeAndCheckError(exprDmc, "goodbye", IFormattedValues.DECIMAL_FORMAT); writeAndCheckError(exprDmc, "abggg", IFormattedValues.HEX_FORMAT); writeAndCheckError(exprDmc, "99", IFormattedValues.OCTAL_FORMAT); writeAndCheckError(exprDmc, "234", IFormattedValues.BINARY_FORMAT); writeAndCheckError(exprDmc, "hello", IFormattedValues.NATURAL_FORMAT); writeAndCheckError(exprDmc, "1", "ThisFormatDoesNotExist"); IExpressionDMContext notWritableExprDmc = SyncUtil.createExpression(frameDmc, "10+5"); writeAndCheckError(notWritableExprDmc, "1", IFormattedValues.NATURAL_FORMAT); } /* * This method does a write that should use an invalid value or format, and * verifies that the operation fails */ private void writeAndCheckError(final IExpressionDMContext exprDmc, final String invalidValueFormatted, final String format) throws Throwable { final AsyncCompletionWaitor wait = new AsyncCompletionWaitor(); // Write the new value using its formatted value fExpService.getExecutor().submit(new Runnable() { public void run() { fExpService.writeExpression(exprDmc, invalidValueFormatted, format, new RequestMonitor(fExpService .getExecutor(), null) { @Override protected void handleCompleted() { wait.waitFinished(getStatus()); } }); } }); wait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); assertTrue("Got an OK status for an error test case. Should not be able to write value " + invalidValueFormatted + " in " + format, !wait.isOK()); assertTrue("ExprChangedEvent problem: expected 0, received " + getExprChangedCount(), getExprChangedCount() == 0); } /** * This test tries multiple format reads during the same executor cycle, to * make sure the internal MI commands are sequenced properly. */ @Test public void testConcurrentReads() throws Throwable { // Next we test that we can read the value more than once // of the same variable object at the exact same time SyncUtil.runToLocation("testConcurrent"); MIStoppedEvent stoppedEvent = SyncUtil.step(1, StepType.STEP_OVER); final IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); final AsyncCompletionWaitor wait = new AsyncCompletionWaitor(); fExpService.getExecutor().submit(new Runnable() { public void run() { IExpressionDMContext exprDmc = fExpService.createExpression(frameDmc, "a[0]"); wait.increment(); fExpService.getFormattedExpressionValue(fExpService.getFormattedValueContext(exprDmc, IFormattedValues.NATURAL_FORMAT), new DataRequestMonitor<FormattedValueDMData>(fExpService .getExecutor(), null) { @Override protected void handleCompleted() { if (!isSuccess()) { wait.waitFinished(getStatus()); } else { if (getData().getFormattedValue().equals("28")) { wait.waitFinished(); } else { wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, "Failed evaluating natural format", null)); } } } }); wait.increment(); fExpService.getFormattedExpressionValue(fExpService.getFormattedValueContext(exprDmc, IFormattedValues.HEX_FORMAT), new DataRequestMonitor<FormattedValueDMData>(fExpService .getExecutor(), null) { @Override protected void handleCompleted() { if (!isSuccess()) { wait.waitFinished(getStatus()); } else { if (getData().getFormattedValue().equalsIgnoreCase("0x1c")) { wait.waitFinished(); } else { wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, "Failed evaluating hex format", null)); } } } }); } }); wait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); assertTrue(wait.getMessage(), wait.isOK()); assertTrue("ExprChangedEvent problem: expected 0, received " + getExprChangedCount(), getExprChangedCount() == 0); } /** * This test tries reads and listChildren during the same executor cycle, to * make sure the internal MI commands are sequenced properly. */ @Test public void testConcurrentReadChildren() throws Throwable { // Next we test that we can retrieve children while reading the value // and vice-versa SyncUtil.runToLocation("testConcurrent"); MIStoppedEvent stoppedEvent = SyncUtil.step(1, StepType.STEP_OVER); final IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); final AsyncCompletionWaitor wait = new AsyncCompletionWaitor(); // First we get the expected value of the array pointer. final IExpressionDMContext addrDmc = SyncUtil.createExpression(frameDmc, "&a"); fExpService.getExecutor().submit(new Runnable() { public void run() { fExpService.getFormattedExpressionValue( fExpService.getFormattedValueContext(addrDmc, IFormattedValues.NATURAL_FORMAT), new DataRequestMonitor<FormattedValueDMData>(fExpService.getExecutor(), null) { @Override protected void handleCompleted() { if (isSuccess()) { wait.setReturnInfo(getData()); } wait.waitFinished(getStatus()); } }); } }); wait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); assertTrue(wait.getMessage(), wait.isOK()); final String actualAddrStr = ((FormattedValueDMData) wait.getReturnInfo()).getFormattedValue(); wait.waitReset(); // Now perform the test fExpService.getExecutor().submit(new Runnable() { public void run() { IExpressionDMContext exprDmc = fExpService.createExpression(frameDmc, "a"); wait.increment(); fExpService.getFormattedExpressionValue(fExpService.getFormattedValueContext(exprDmc, IFormattedValues.NATURAL_FORMAT), new DataRequestMonitor<FormattedValueDMData>(fExpService .getExecutor(), null) { @Override protected void handleCompleted() { if (!isSuccess()) { wait.waitFinished(getStatus()); } else { if (getData().getFormattedValue().equals(actualAddrStr)) { wait.waitFinished(); } else { wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, "Failed evaluating natural format", null)); } } } }); wait.increment(); fExpService.getSubExpressions(exprDmc, new DataRequestMonitor<IExpressionDMContext[]>( fExpService.getExecutor(), null) { @Override protected void handleCompleted() { if (!isSuccess()) { wait.waitFinished(getStatus()); } else { IExpressionDMContext[] children = getData(); int failedIndex = -1; for (int i = 0; i < 2; i++) { if (!children[i].getExpression().equals("a[" + i + "]")) { failedIndex = i; } } if (failedIndex != -1) { wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, "Failed getting child number: " + failedIndex, null)); } else { wait.waitFinished(); } } } }); // Use different format to avoid triggering the cache wait.increment(); fExpService.getFormattedExpressionValue(fExpService.getFormattedValueContext(exprDmc, IFormattedValues.HEX_FORMAT), new DataRequestMonitor<FormattedValueDMData>(fExpService .getExecutor(), null) { @Override protected void handleCompleted() { if (!isSuccess()) { wait.waitFinished(getStatus()); } else { if (getData().getFormattedValue().equals(actualAddrStr)) { wait.waitFinished(); } else { wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, "Failed evaluating hex format", null)); } } } }); } }); wait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); assertTrue(wait.getMessage(), wait.isOK()); assertTrue("ExprChangedEvent problem: expected 0, received " + getExprChangedCount(), getExprChangedCount() == 0); } /** * This test tries reads and getChildrenCount during the same executor * cycle, to make sure the internal MI commands are sequenced properly. */ @Test public void testConcurrentReadChildrenCount() throws Throwable { // Next we test that we can retrieve children count while reading the // value and vice-versa SyncUtil.runToLocation("testConcurrent"); MIStoppedEvent stoppedEvent = SyncUtil.step(1, StepType.STEP_OVER); final IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); final AsyncCompletionWaitor wait = new AsyncCompletionWaitor(); // First we get the expected value of the array pointer. final IExpressionDMContext addrDmc = SyncUtil.createExpression(frameDmc, "&a"); fExpService.getExecutor().submit(new Runnable() { public void run() { fExpService.getFormattedExpressionValue( fExpService.getFormattedValueContext(addrDmc, IFormattedValues.NATURAL_FORMAT), new DataRequestMonitor<FormattedValueDMData>(fExpService.getExecutor(), null) { @Override protected void handleCompleted() { if (isSuccess()) { wait.setReturnInfo(getData()); } wait.waitFinished(getStatus()); } }); } }); wait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); assertTrue(wait.getMessage(), wait.isOK()); final String actualAddrStr = ((FormattedValueDMData) wait.getReturnInfo()).getFormattedValue(); wait.waitReset(); fExpService.getExecutor().submit(new Runnable() { public void run() { wait.increment(); IExpressionDMContext exprDmc = fExpService.createExpression(frameDmc, "a"); fExpService.getFormattedExpressionValue(fExpService.getFormattedValueContext(exprDmc, IFormattedValues.NATURAL_FORMAT), new DataRequestMonitor<FormattedValueDMData>(fExpService .getExecutor(), null) { @Override protected void handleCompleted() { if (!isSuccess()) { wait.waitFinished(getStatus()); } else { if (getData().getFormattedValue().equals(actualAddrStr)) { wait.waitFinished(); } else { wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, "Failed evaluating natural format", null)); } } } }); wait.increment(); fExpService.getSubExpressionCount(exprDmc, new DataRequestMonitor<Integer>(fExpService.getExecutor(), null) { @Override protected void handleCompleted() { if (!isSuccess()) { wait.waitFinished(getStatus()); } else { int count = getData(); if (count != 2) { wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, "Failed getting count for children. Got" + count + "instead of 2", null)); } else { wait.waitFinished(); } } } }); // Use different format to avoid triggering the cache wait.increment(); fExpService.getFormattedExpressionValue(fExpService.getFormattedValueContext(exprDmc, IFormattedValues.HEX_FORMAT), new DataRequestMonitor<FormattedValueDMData>(fExpService .getExecutor(), null) { @Override protected void handleCompleted() { if (!isSuccess()) { wait.waitFinished(getStatus()); } else { if (getData().getFormattedValue().equals(actualAddrStr)) { wait.waitFinished(); } else { wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, "Failed evaluating hex format", null)); } } } }); } }); wait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); assertTrue(wait.getMessage(), wait.isOK()); assertTrue("ExprChangedEvent problem: expected 0, received " + getExprChangedCount(), getExprChangedCount() == 0); } /** * This test tries reads and writes during the same executor cycle, to make * sure the internal MI commands are sequenced properly. */ @Test public void testConcurrentReadWrite() throws Throwable { // Next we test that we can deal with a write request and read request // at // the same time and vice-versa SyncUtil.runToLocation("testConcurrent"); MIStoppedEvent stoppedEvent = SyncUtil.step(1, StepType.STEP_OVER); IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); final IExpressionDMContext exprDmc = SyncUtil.createExpression(frameDmc, "a[1]"); final AsyncCompletionWaitor wait = new AsyncCompletionWaitor(); fExpService.getExecutor().submit(new Runnable() { public void run() { wait.increment(); fExpService.getFormattedExpressionValue(fExpService.getFormattedValueContext(exprDmc, IFormattedValues.NATURAL_FORMAT), new DataRequestMonitor<FormattedValueDMData>(fExpService .getExecutor(), null) { @Override protected void handleCompleted() { if (!isSuccess()) { wait.waitFinished(getStatus()); } else { if (getData().getFormattedValue().equals("32")) { wait.waitFinished(); } else { wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, "Failed evaluating natural format, got " + getData().getFormattedValue() + " instead of 32", null)); } } } }); wait.increment(); fExpService.writeExpression(exprDmc, "56", IFormattedValues.NATURAL_FORMAT, new RequestMonitor( fExpService.getExecutor(), null) { @Override protected void handleCompleted() { if (!isSuccess()) { wait.waitFinished(getStatus()); } else { wait.waitFinished(); } } }); // Use different format to avoid triggering the cache wait.increment(); fExpService.getFormattedExpressionValue(fExpService.getFormattedValueContext(exprDmc, IFormattedValues.HEX_FORMAT), new DataRequestMonitor<FormattedValueDMData>(fExpService .getExecutor(), null) { @Override protected void handleCompleted() { if (!isSuccess()) { wait.waitFinished(getStatus()); } else { if (getData().getFormattedValue().equals("0x38")) { wait.waitFinished(); } else { wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, "Failed evaluating hex format, got " + getData().getFormattedValue() + " instead of 0x38", null)); } } } }); } }); wait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); assertTrue(wait.getMessage(), wait.isOK()); assertTrue("ExprChangedEvent problem: expected 1, received " + getExprChangedCount(), getExprChangedCount() == 1); exprDmc.equals(getExprChangedContext()); clearExprChangedData(); } /** * This test tries many different operations during the same executor cycle, * to make sure the internal MI commands are sequenced properly. */ @Test public void testConcurrentReadWriteChildren() throws Throwable { // Finally, we go nuts and request two reads, while requesting // a get children and get children count. // Note that we don't request a write, because a write is allowed to // go through at any time and we don't exactly know when it will // change the value we are reading. SyncUtil.runToLocation("testConcurrent"); MIStoppedEvent stoppedEvent = SyncUtil.step(1, StepType.STEP_OVER); IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); final IExpressionDMContext exprDmc = SyncUtil.createExpression(frameDmc, "a[1]"); final AsyncCompletionWaitor wait = new AsyncCompletionWaitor(); fExpService.getExecutor().submit(new Runnable() { public void run() { wait.increment(); fExpService.getFormattedExpressionValue(fExpService.getFormattedValueContext(exprDmc, IFormattedValues.NATURAL_FORMAT), new DataRequestMonitor<FormattedValueDMData>(fExpService .getExecutor(), null) { @Override protected void handleCompleted() { if (!isSuccess()) { wait.waitFinished(getStatus()); } else { if (getData().getFormattedValue().equals("32")) { wait.waitFinished(); } else { wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, "Failed evaluating natural format, got " + getData().getFormattedValue() + " instead of 32", null)); } } } }); wait.increment(); fExpService.getFormattedExpressionValue(fExpService.getFormattedValueContext(exprDmc, IFormattedValues.HEX_FORMAT), new DataRequestMonitor<FormattedValueDMData>(fExpService .getExecutor(), null) { @Override protected void handleCompleted() { if (!isSuccess()) { wait.waitFinished(getStatus()); } else { if (getData().getFormattedValue().equals("0x20")) { wait.waitFinished(); } else { wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, "Failed evaluating hex format, got " + getData().getFormattedValue() + " instead of 0x20", null)); } } } }); wait.increment(); fExpService.getSubExpressionCount(exprDmc, new DataRequestMonitor<Integer>(fExpService.getExecutor(), null) { @Override protected void handleCompleted() { if (!isSuccess()) { wait.waitFinished(getStatus()); } else { if (getData() != 0) { wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, "Failed getting child count; expecting 0 got " + getData(), null)); } else { wait.waitFinished(); } } } }); wait.increment(); fExpService.getSubExpressions(exprDmc, new DataRequestMonitor<IExpressionDMContext[]>( fExpService.getExecutor(), null) { @Override protected void handleCompleted() { if (!isSuccess()) { wait.waitFinished(getStatus()); } else { if (getData().length != 0) { wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, "Failed getting children; expecting 0 got " + getData().length, null)); } else { wait.waitFinished(); } } } }); // Must use a different format or else the cache will be triggered wait.increment(); fExpService.getFormattedExpressionValue(fExpService.getFormattedValueContext(exprDmc, IFormattedValues.OCTAL_FORMAT), new DataRequestMonitor<FormattedValueDMData>(fExpService .getExecutor(), null) { @Override protected void handleCompleted() { if (!isSuccess()) { wait.waitFinished(getStatus()); } else { if (getData().getFormattedValue().equals("040")) { wait.waitFinished(); } else { wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, "Failed evaluating hex format, got " + getData().getFormattedValue() + " instead of 040", null)); } } } }); } }); wait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); assertTrue(wait.getMessage(), wait.isOK()); assertTrue("ExprChangedEvent problem: expected 0, received " + getExprChangedCount(), getExprChangedCount() == 0); exprDmc.equals(getExprChangedContext()); clearExprChangedData(); } /** * This test verifies that the ExpressionService caches the evaluation of an * expression in a specific format. It verifies this by: 1- reading a * variable 2- writing to that variable 3- reading the variable in a new * format and seeing the new value 4- reading the variable in the same * format as step 1 and seeing the old value cached Note that all above * steps must be done within the same Runnable submitted to the executor. * This allows the cache to be triggered before it is invalidated by a write * command, since the write command will need an new executor cycle to send * an MI command to the back-end */ @Test public void testWriteCache() throws Throwable { // Test the cache by changing a value but triggering a read before the // write clears the cache SyncUtil.runToLocation("testConcurrent"); MIStoppedEvent stoppedEvent = SyncUtil.step(1, StepType.STEP_OVER); IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); final IExpressionDMContext exprDmc = SyncUtil.createExpression(frameDmc, "a[1]"); final AsyncCompletionWaitor wait = new AsyncCompletionWaitor(); fExpService.getExecutor().submit(new Runnable() { public void run() { wait.increment(); fExpService.getFormattedExpressionValue(fExpService.getFormattedValueContext(exprDmc, IFormattedValues.NATURAL_FORMAT), new DataRequestMonitor<FormattedValueDMData>(fExpService .getExecutor(), null) { @Override protected void handleCompleted() { if (!isSuccess()) { wait.waitFinished(getStatus()); } else { if (getData().getFormattedValue().equals("32")) { wait.waitFinished(); } else { wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, "Failed evaluating natural format, got " + getData().getFormattedValue() + " instead of 32", null)); } } } }); wait.increment(); fExpService.writeExpression(exprDmc, "56", IFormattedValues.NATURAL_FORMAT, new RequestMonitor( fExpService.getExecutor(), null) { @Override protected void handleCompleted() { if (!isSuccess()) { wait.waitFinished(getStatus()); } else { wait.waitFinished(); } } }); // Must use a different format or else the cache will be // triggered // This will prove that the write has changed the backend wait.increment(); fExpService.getFormattedExpressionValue(fExpService.getFormattedValueContext(exprDmc, IFormattedValues.OCTAL_FORMAT), new DataRequestMonitor<FormattedValueDMData>(fExpService .getExecutor(), null) { @Override protected void handleCompleted() { if (!isSuccess()) { wait.waitFinished(getStatus()); } else { if (getData().getFormattedValue().equals("070")) { wait.waitFinished(); } else { wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, "Failed evaluating hex format, got " + getData().getFormattedValue() + " instead of 070", null)); } } } }); // Test that the cache is triggered, giving us the old value // This happens because we are calling this operation on the // same executor run call. // NOTE that this is not a problem, because the writeExpression // will eventually // reset the cache (we'll test this below). wait.increment(); fExpService.getFormattedExpressionValue(fExpService.getFormattedValueContext(exprDmc, IFormattedValues.NATURAL_FORMAT), new DataRequestMonitor<FormattedValueDMData>(fExpService .getExecutor(), null) { @Override protected void handleCompleted() { if (!isSuccess()) { wait.waitFinished(getStatus()); } else { if (getData().getFormattedValue().equals("32")) { wait.waitFinished(); } else { wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, "Failed evaluating natural format, got " + getData().getFormattedValue() + " instead of 32", null)); } } } }); } }); wait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); assertTrue(wait.getMessage(), wait.isOK()); // Now that we know the writeExpressions completed and the cache was // reset, do a similar // request as above to see that the cache has indeed been reset wait.waitReset(); fExpService.getExecutor().submit(new Runnable() { public void run() { wait.increment(); fExpService.getFormattedExpressionValue(fExpService.getFormattedValueContext(exprDmc, IFormattedValues.NATURAL_FORMAT), new DataRequestMonitor<FormattedValueDMData>(fExpService .getExecutor(), null) { @Override protected void handleCompleted() { if (!isSuccess()) { wait.waitFinished(getStatus()); } else { if (getData().getFormattedValue().equals("56")) { wait.waitFinished(); } else { wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, "Failed evaluating natural format, got " + getData().getFormattedValue() + " instead of 56", null)); } } } }); } }); wait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); assertTrue(wait.getMessage(), wait.isOK()); assertTrue("ExprChangedEvent problem: expected 1, received " + getExprChangedCount(), getExprChangedCount() == 1); exprDmc.equals(getExprChangedContext()); clearExprChangedData(); } /** * Test that we can correctly retrieve the address and type size of an * expression */ @Test public void testExprAddress() throws Throwable { SyncUtil.runToLocation("testAddress"); MIStoppedEvent stoppedEvent = SyncUtil.step(2, StepType.STEP_OVER); IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); final IExpressionDMContext exprDmc = SyncUtil.createExpression(frameDmc, "a"); final IExpressionDMContext exprDmc2 = SyncUtil.createExpression(frameDmc, "a_ptr"); final AsyncCompletionWaitor wait = new AsyncCompletionWaitor(); // First get the address of 'a' through 'a_ptr' fExpService.getExecutor().submit(new Runnable() { public void run() { fExpService.getFormattedExpressionValue(fExpService.getFormattedValueContext(exprDmc2, IFormattedValues.NATURAL_FORMAT), new DataRequestMonitor<FormattedValueDMData>(fExpService .getExecutor(), null) { @Override protected void handleCompleted() { if (isSuccess()) { wait.setReturnInfo(getData()); } wait.waitFinished(getStatus()); } }); } }); wait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); assertTrue(wait.getMessage(), wait.isOK()); String actualAddrStr = ((FormattedValueDMData) wait.getReturnInfo()).getFormattedValue(); wait.waitReset(); // Now check the address through our getAddressData checkAddressData(exprDmc, actualAddrStr, 4); assertTrue("ExprChangedEvent problem: expected 0, received " + getExprChangedCount(), getExprChangedCount() == 0); } /** * Test that we can correctly evaluate C expressions involving global * variables. * * @return void */ @Test public void testGlobalVariables() throws Throwable { // Step to a stack level of 2 to be able to test differen stack frames MIStoppedEvent stoppedEvent = SyncUtil.runToLocation("locals2"); // Create a map of expressions to expected values. Map<String, String[]> tests = new HashMap<String, String[]>(); // Global variables tests.put("gIntVar", new String[] { "0x21F", "01037", "1000011111", "543", "543", "543" }); tests.put("gDoubleVar", new String[] { "0x21F", "01037", "1000011111", "543", "543.54300000000001", "543.54300000000001" }); tests.put("gCharVar", new String[] { "0x67", "0147", "1100111", "103", "103 'g'", "103 'g'" }); tests.put("gBoolVar", new String[] { "0x0", "0", "0", "0", "false", "false" }); tests.put("gIntArray[1]", new String[] { "0x28E", "01216", "1010001110", "654", "654", "654" }); tests.put("gDoubleArray[1]", new String[] { "0x28E", "01216", "1010001110", "654", "654.32100000000003", "654.32100000000003" }); tests.put("gCharArray[1]", new String[] { "0x64", "0144", "1100100", "100", "100 'd'", "100 'd'" }); tests.put("gBoolArray[1]", new String[] { "0x0", "0", "0", "0", "false", "false" }); tests.put("*gIntPtr", new String[] { "0x21F", "01037", "1000011111", "543", "543", "543" }); tests.put("*gDoublePtr", new String[] { "0x21F", "01037", "1000011111", "543", "543.54300000000001", "543.54300000000001" }); tests.put("*gCharPtr", new String[] { "0x67", "0147", "1100111", "103", "103 'g'", "103 'g'" }); tests.put("*gBoolPtr", new String[] { "0x0", "0", "0", "0", "false", "false" }); tests.put("gIntPtr2", new String[] { "0x8", "010", "1000", "8", "0x8" , "0x8" }); tests.put("gDoublePtr2", new String[] { "0x5432", "052062", "101010000110010", "21554", "0x5432", "0x5432" }); // GDB says a char* is out of bounds, but not the other pointers??? // tests.put("gCharPtr2", new String[] { "0x4321", "041441", // "100001100100001", "17185", "0x4321" }); tests.put("gBoolPtr2", new String[] { "0x12ABCDEF", "02252746757", "10010101010111100110111101111", "313249263", "0x12ABCDEF", "0x12ABCDEF" }); // Try different stack frames executeExpressionSubTests(tests, SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0)); executeExpressionSubTests(tests, SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 1)); executeExpressionSubTests(tests, SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 2)); } /** * This test verifies that the ExpressionService can handle having a * variable with the same name in two different methods but at the same * stack depth. */ @Test public void testNamingSameDepth() throws Throwable { SyncUtil.runToLocation("testName1"); MIStoppedEvent stoppedEvent = SyncUtil.step(1, StepType.STEP_OVER); IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); Map<String, String[]> tests = new HashMap<String, String[]>(); tests.put("a", new String[] { "0x1", "01", "1", "1", "1", "1" }); executeExpressionSubTests(tests, frameDmc); SyncUtil.runToLocation("testName2"); stoppedEvent = SyncUtil.step(1, StepType.STEP_INTO); frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); tests = new HashMap<String, String[]>(); tests.put("a", new String[] { "0x2", "02", "10", "2", "2", "2" }); executeExpressionSubTests(tests, frameDmc); SyncUtil.runToLocation("testName1"); stoppedEvent = SyncUtil.step(1, StepType.STEP_INTO); frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); tests = new HashMap<String, String[]>(); tests.put("a", new String[] { "0x3", "03", "11", "3", "3", "3" }); executeExpressionSubTests(tests, frameDmc); } /** * This test verifies that the ExpressionService can handle having a * variable with the same name in two methods that also have the same name */ @Test public void testNamingSameMethod() throws Throwable { SyncUtil.runToLocation("testSameName"); MIStoppedEvent stoppedEvent = SyncUtil.step(2, StepType.STEP_INTO); IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); Map<String, String[]> tests = new HashMap<String, String[]>(); tests.put("a", new String[] { "0x1", "01", "1", "1", "1" , "1" }); executeExpressionSubTests(tests, frameDmc); SyncUtil.step(StepType.STEP_RETURN); stoppedEvent = SyncUtil.step(2, StepType.STEP_INTO); frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); tests = new HashMap<String, String[]>(); tests.put("a", new String[] { "0x2", "02", "10", "2", "2", "2" }); executeExpressionSubTests(tests, frameDmc); SyncUtil.step(StepType.STEP_RETURN); stoppedEvent = SyncUtil.step(2, StepType.STEP_INTO); frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); tests = new HashMap<String, String[]>(); tests.put("a", new String[] { "0x3", "03", "11", "3", "3", "3" }); executeExpressionSubTests(tests, frameDmc); } /** * This test makes sure that if a request for expression values are made with * a thread selected, the top-most stack frame is used for evaluation */ @Test public void testThreadContext() throws Throwable { // Step to a stack level of 2 to be able to test differen stack frames SyncUtil.runToLocation("locals2"); MIStoppedEvent stoppedEvent = SyncUtil.step(StepType.STEP_OVER); // Create a map of expressions to expected values. Map<String, String[]> tests = new HashMap<String, String[]>(); // First make sure we have a different value on the other stack frame and that we select // a frame that is not the top frame tests.put("lIntVar", new String[] { "0x3039", "030071", "11000000111001", "12345", "12345", "12345" }); executeExpressionSubTests(tests, SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 1)); // Now check that we get the same values as the top stack when selecting the thread only tests = new HashMap<String, String[]>(); tests.put("lIntVar", new String[] { "0x1a85", "015205", "1101010000101", "6789", "6789" , "6789" }); executeExpressionSubTests(tests, stoppedEvent.getDMContext()); } /** * This test verifies that the ExpressionService can handle having a * child variable with the same name in two methods that also have the same name */ @Test public void testChildNamingSameMethod() throws Throwable { SyncUtil.runToLocation("testSameName"); MIStoppedEvent stoppedEvent = SyncUtil.step(4, StepType.STEP_INTO); final IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); final AsyncCompletionWaitor wait = new AsyncCompletionWaitor(); fExpService.getExecutor().submit(new Runnable() { public void run() { // First create the var object and all its children IExpressionDMContext parentDmc = fExpService.createExpression(frameDmc, "z"); fExpService.getSubExpressions( parentDmc, new DataRequestMonitor<IExpressionDMContext[]>(fExpService.getExecutor(), null) { @Override protected void handleCompleted() { if (!isSuccess()) { wait.waitFinished(getStatus()); } else if (getData().length != 2) { wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, "Failed getting children; expecting 2 got " + getData().length, null)); } else { // now get the value of the child final String valueStr = "1"; final IExpressionDMContext child = getData()[0]; fExpService.getFormattedExpressionValue( fExpService.getFormattedValueContext(child, IFormattedValues.NATURAL_FORMAT), new DataRequestMonitor<FormattedValueDMData>(fExpService.getExecutor(), null) { @Override protected void handleCompleted() { if (!isSuccess()) { wait.waitFinished(getStatus()); } else if (getData().getFormattedValue().equals(valueStr)) { wait.waitFinished(); } else { wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, "Failed evaluating " + child.getExpression() + ", got " + getData().getFormattedValue() + " instead of " + valueStr, null)); } } }); } } }); } }); wait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); assertTrue(wait.getMessage(), wait.isOK()); wait.waitReset(); SyncUtil.step(StepType.STEP_RETURN); stoppedEvent = SyncUtil.step(4, StepType.STEP_INTO); final IFrameDMContext frameDmc2 = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); fExpService.getExecutor().submit(new Runnable() { public void run() { // First create the var object and all its children IExpressionDMContext parentDmc = fExpService.createExpression(frameDmc2, "z"); fExpService.getSubExpressions( parentDmc, new DataRequestMonitor<IExpressionDMContext[]>(fExpService.getExecutor(), null) { @Override protected void handleCompleted() { if (!isSuccess()) { wait.waitFinished(getStatus()); } else if (getData().length != 2) { wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, "Failed getting children; expecting 2 got " + getData().length, null)); } else { // now get the value of the child final String valueStr = "2"; final IExpressionDMContext child = getData()[0]; fExpService.getFormattedExpressionValue( fExpService.getFormattedValueContext(child, IFormattedValues.NATURAL_FORMAT), new DataRequestMonitor<FormattedValueDMData>(fExpService.getExecutor(), null) { @Override protected void handleCompleted() { if (!isSuccess()) { wait.waitFinished(getStatus()); } else if (getData().getFormattedValue().equals(valueStr)) { wait.waitFinished(); } else { wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, "Failed evaluating " + child.getExpression() + ", got " + getData().getFormattedValue() + " instead of " + valueStr, null)); } } }); } } }); } }); wait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); assertTrue(wait.getMessage(), wait.isOK()); wait.waitReset(); SyncUtil.step(StepType.STEP_RETURN); stoppedEvent = SyncUtil.step(4, StepType.STEP_INTO); final IFrameDMContext frameDmc3 = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); fExpService.getExecutor().submit(new Runnable() { public void run() { // First create the var object and all its children IExpressionDMContext parentDmc = fExpService.createExpression(frameDmc3, "z"); fExpService.getSubExpressions( parentDmc, new DataRequestMonitor<IExpressionDMContext[]>(fExpService.getExecutor(), null) { @Override protected void handleCompleted() { if (!isSuccess()) { wait.waitFinished(getStatus()); } else if (getData().length != 2) { wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, "Failed getting children; expecting 2 got " + getData().length, null)); } else { // now get the value of the child final String valueStr = "3"; final IExpressionDMContext child = getData()[0]; fExpService.getFormattedExpressionValue( fExpService.getFormattedValueContext(child, IFormattedValues.NATURAL_FORMAT), new DataRequestMonitor<FormattedValueDMData>(fExpService.getExecutor(), null) { @Override protected void handleCompleted() { if (!isSuccess()) { wait.waitFinished(getStatus()); } else if (getData().getFormattedValue().equals(valueStr)) { wait.waitFinished(); } else { wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, "Failed evaluating " + child.getExpression() + ", got " + getData().getFormattedValue() + " instead of " + valueStr, null)); } } }); } } }); } }); wait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); assertTrue(wait.getMessage(), wait.isOK()); wait.waitReset(); } /** * This test verifies that the ExpressionService properly updates * children variables, when we do not update the parent explicitly */ @Test public void testUpdatingChildren() throws Throwable { SyncUtil.runToLocation("testUpdateChildren"); MIStoppedEvent stoppedEvent = SyncUtil.step(2, StepType.STEP_OVER); final IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); doUpdateTest(frameDmc, 0); // Re-run the test to test out-of-scope update again SyncUtil.step(StepType.STEP_RETURN); stoppedEvent = SyncUtil.step(3, StepType.STEP_INTO); final IFrameDMContext frameDmc2 = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); doUpdateTest(frameDmc2, 100); // Re-run the test within a different method test out-of-scope updates SyncUtil.step(StepType.STEP_RETURN); stoppedEvent = SyncUtil.step(3, StepType.STEP_INTO); final IFrameDMContext frameDmc3 = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); doUpdateTest(frameDmc3, 200); } public void doUpdateTest(final IFrameDMContext frameDmc, final int baseValue) throws Throwable { final AsyncCompletionWaitor wait = new AsyncCompletionWaitor(); fExpService.getExecutor().submit(new Runnable() { public void run() { // First create the var object and all its children IExpressionDMContext parentDmc = fExpService.createExpression(frameDmc, "a"); fExpService.getSubExpressions( parentDmc, new DataRequestMonitor<IExpressionDMContext[]>(fExpService.getExecutor(), null) { @Override protected void handleCompleted() { if (!isSuccess()) { wait.waitFinished(getStatus()); } else if (getData().length != 1) { wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, "Failed getting children; expecting 1 got " + getData().length, null)); } else { // Now list the children of this child fExpService.getSubExpressions( getData()[0], new DataRequestMonitor<IExpressionDMContext[]>(fExpService.getExecutor(), null) { @Override protected void handleCompleted() { final IExpressionDMContext[] childDmcs = getData(); if (!isSuccess()) { wait.waitFinished(getStatus()); } else if (childDmcs.length != 2) { wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, "Failed getting children; expecting 2 got " + childDmcs.length, null)); } else { // now get the value of the two children for (int i =0; i<2; i++) { final String valueStr = Integer.toString(baseValue + i + 10); final int finali = i; wait.increment(); fExpService.getFormattedExpressionValue( fExpService.getFormattedValueContext(childDmcs[i], IFormattedValues.NATURAL_FORMAT), new DataRequestMonitor<FormattedValueDMData>(fExpService.getExecutor(), null) { @Override protected void handleCompleted() { if (!isSuccess()) { wait.waitFinished(getStatus()); } else if (getData().getFormattedValue().equals(valueStr)) { wait.waitFinished(); } else { wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, "Failed evaluating " + childDmcs[finali].getExpression() + ", got " + getData().getFormattedValue() + " instead of " + valueStr, null)); } } }); } } } }); } } }); } }); wait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); assertTrue(wait.getMessage(), wait.isOK()); wait.waitReset(); // Now step to change the value of a.z.x and a.z.y and verify the changed values. // This will confirm that the parent "a" will have been properly updated // It is a better test to do it for two children because it tests concurrent update requests MIStoppedEvent stoppedEvent = SyncUtil.step(2, StepType.STEP_OVER); final IFrameDMContext frameDmc2 = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); fExpService.getExecutor().submit(new Runnable() { public void run() { // First create the var object and all its children IExpressionDMContext parentDmc = fExpService.createExpression(frameDmc2, "a"); fExpService.getSubExpressions( parentDmc, new DataRequestMonitor<IExpressionDMContext[]>(fExpService.getExecutor(), null) { @Override protected void handleCompleted() { if (!isSuccess()) { wait.waitFinished(getStatus()); } else if (getData().length != 1) { wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, "Failed getting children; expecting 1 got " + getData().length, null)); } else { // Now list the children of this child fExpService.getSubExpressions( getData()[0], new DataRequestMonitor<IExpressionDMContext[]>(fExpService.getExecutor(), null) { @Override protected void handleCompleted() { final IExpressionDMContext[] childDmcs = getData(); if (!isSuccess()) { wait.waitFinished(getStatus()); } else if (childDmcs.length != 2) { wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, "Failed getting children; expecting 2 got " + childDmcs.length, null)); } else { // now get the value of the two children for (int i =0; i<2; i++) { final String valueStr = Integer.toString(baseValue + i + 20); final int finali = i; wait.increment(); fExpService.getFormattedExpressionValue( fExpService.getFormattedValueContext(childDmcs[i], IFormattedValues.NATURAL_FORMAT), new DataRequestMonitor<FormattedValueDMData>(fExpService.getExecutor(), null) { @Override protected void handleCompleted() { if (!isSuccess()) { wait.waitFinished(getStatus()); } else if (getData().getFormattedValue().equals(valueStr)) { wait.waitFinished(); } else { wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, "Failed evaluating " + childDmcs[finali].getExpression() + ", got " + getData().getFormattedValue() + " instead of " + valueStr, null)); } } }); } } } }); } } }); } }); wait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); assertTrue(wait.getMessage(), wait.isOK()); wait.waitReset(); } /** * This test creates a variable object with children (not an array) and then gets these children * to be deleted because of a large number of other variable objects being created. * We then check that the expression service can handle a request for one of those deleted children, * which has a complex path. */ @Test public void testDeleteChildren() throws Throwable { SyncUtil.runToLocation("testDeleteChildren"); MIStoppedEvent stoppedEvent = SyncUtil.step(1, StepType.STEP_OVER); final IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); final AsyncCompletionWaitor wait = new AsyncCompletionWaitor(); fExpService.getExecutor().submit(new Runnable() { public void run() { // First create the var object and all its children IExpressionDMContext parentDmc = fExpService.createExpression(frameDmc, "f"); fExpService.getSubExpressions( parentDmc, new DataRequestMonitor<IExpressionDMContext[]>(fExpService.getExecutor(), null) { @Override protected void handleCompleted() { if (!isSuccess()) { wait.waitFinished(getStatus()); } else { if (getData().length != 5) { wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, "Failed getting children; expecting 5 got " + getData().length, null)); } else { String childStr = "((bar) f)"; if (!getData()[0].getExpression().equals(childStr)) { wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, "Got child " + getData()[0].getExpression() + " instead of " + childStr, null)); } else { // Now list the children of the first element fExpService.getSubExpressions( getData()[0], new DataRequestMonitor<IExpressionDMContext[]>(fExpService.getExecutor(), null) { @Override protected void handleCompleted() { if (!isSuccess()) { wait.waitFinished(getStatus()); } else { if (getData().length != 2) { wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, "Failed getting children; expecting 2 got " + getData().length, null)); } else { String childStr = "((((bar) f)).d)"; if (!getData()[0].getExpression().equals(childStr)) { wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, "Got child " + getData()[0].getExpression() + " instead of " + childStr, null)); } else { wait.setReturnInfo(getData()[0]); wait.waitFinished(); } } } } }); } } } } }); } }); wait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); assertTrue(wait.getMessage(), wait.isOK()); final IExpressionDMContext deletedChildDmc = (IExpressionDMContext)wait.getReturnInfo(); wait.waitReset(); fExpService.getExecutor().submit(new Runnable() { public void run() { // Now create more than 1000 expressions to trigger the deletion of the children // that were created above for (int i=0; i<1100; i++) { IExpressionDMContext dmc = fExpService.createExpression(frameDmc, "a[" + i + "]"); wait.increment(); fExpService.getExpressionData( dmc, new DataRequestMonitor<IExpressionDMData>(fExpService.getExecutor(), null) { @Override protected void handleCompleted() { if (!isSuccess()) { wait.waitFinished(getStatus()); } else { wait.waitFinished(); } } }); } } }); wait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); assertTrue(wait.getMessage(), wait.isOK()); wait.waitReset(); fExpService.getExecutor().submit(new Runnable() { public void run() { // Evaluate the expression of a child that we know is deleted to make sure // the expression service can handle that fExpService.getExpressionData( deletedChildDmc, new DataRequestMonitor<IExpressionDMData>(fExpService.getExecutor(), null) { @Override protected void handleCompleted() { if (!isSuccess()) { wait.waitFinished(getStatus()); } else { wait.waitFinished(); } } }); } }); wait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); assertTrue(wait.getMessage(), wait.isOK()); wait.waitReset(); } /** * GDB 6.7 has a bug which will cause var-update not to show * the new value of 'a' if we switch the format to binary, * since binary of 3 is 11 which is the same as the old value * in natural format. Our expression service should work around this. * * int main() { * int a = 11; * a = 3; * return 0; * } */ @Test public void testUpdateGDBBug() throws Throwable { SyncUtil.runToLocation("testUpdateGDBBug"); MIStoppedEvent stoppedEvent = SyncUtil.step(1, StepType.STEP_OVER); final IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); final AsyncCompletionWaitor wait = new AsyncCompletionWaitor(); fExpService.getExecutor().submit(new Runnable() { public void run() { // First create the var object and all its children IExpressionDMContext exprDmc = fExpService.createExpression(frameDmc, "a"); // This call will create the variable object in natural format and then change // it to binary to fetch the value fExpService.getFormattedExpressionValue( fExpService.getFormattedValueContext(exprDmc, IFormattedValues.BINARY_FORMAT), new DataRequestMonitor<FormattedValueDMData>(fExpService.getExecutor(), null) { @Override protected void handleCompleted() { if (!isSuccess()) { wait.waitFinished(getStatus()); } else { if (getData().getFormattedValue().equals("1011")) { wait.waitFinished(); } else { wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, "Failed evaluating binary format, expected 1011 but got " + getData().getFormattedValue(), null)); } } } }); } }); wait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); assertTrue(wait.getMessage(), wait.isOK()); wait.waitReset(); // Now step to change the value of "a" and ask for it again stoppedEvent = SyncUtil.step(1, StepType.STEP_OVER); final IFrameDMContext frameDmc2 = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); fExpService.getExecutor().submit(new Runnable() { public void run() { // First create the var object and all its children IExpressionDMContext exprDmc = fExpService.createExpression(frameDmc2, "a"); // This call will create the variable object in natural format and then change // it to binary to fetch the value fExpService.getFormattedExpressionValue( fExpService.getFormattedValueContext(exprDmc, IFormattedValues.BINARY_FORMAT), new DataRequestMonitor<FormattedValueDMData>(fExpService.getExecutor(), null) { @Override protected void handleCompleted() { if (!isSuccess()) { wait.waitFinished(getStatus()); } else { if (getData().getFormattedValue().equals("11")) { wait.waitFinished(); } else { wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, "Failed evaluating binary format, expected 11 but got " + getData().getFormattedValue(), null)); } } } }); } }); wait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); assertTrue(wait.getMessage(), wait.isOK()); wait.waitReset(); } /** * var-update will not show a change if eval-expression is the same * in the current format. This is a problem for us because we don't * know if another format changed: * * int main() { * double a = 1.99; * a = 1.11; * } * * If a is displayed in anything but natural, both values of a are the same * and we won't know it changed in the natural format. * * The test below is in case GDB fixes var-update to keep track of the last * printed value through eval-expression. Until they do that, we do not have * a problem because of our caching: where, if we change formats since the last * var-update, it is impossible for us to set the format back * to the one of the last -var-update, since we already have that value in our cache. * So, the -var-update will show a change because of the new current format. * But if GDB has eval-expression reset their stored printed_value, this test * will fail and we'll know we have to fix something. */ @Test public void testUpdateIssue() throws Throwable { SyncUtil.runToLocation("testUpdateIssue"); MIStoppedEvent stoppedEvent = SyncUtil.step(1, StepType.STEP_OVER); final IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); final AsyncCompletionWaitor wait = new AsyncCompletionWaitor(); fExpService.getExecutor().submit(new Runnable() { public void run() { // First create the var object and all its children IExpressionDMContext exprDmc = fExpService.createExpression(frameDmc, "a"); // check that we have the proper value wait.increment(); fExpService.getFormattedExpressionValue( fExpService.getFormattedValueContext(exprDmc, IFormattedValues.NATURAL_FORMAT), new DataRequestMonitor<FormattedValueDMData>(fExpService.getExecutor(), null) { @Override protected void handleCompleted() { if (!isSuccess()) { wait.waitFinished(getStatus()); } else { if (getData().getFormattedValue().equals("1.99")) { wait.waitFinished(); } else { wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, "Failed evaluating a, expected 1.99 but got " + getData().getFormattedValue(), null)); } } } }); // ask for hex to set the format to hex wait.increment(); fExpService.getFormattedExpressionValue( fExpService.getFormattedValueContext(exprDmc, IFormattedValues.HEX_FORMAT), new DataRequestMonitor<FormattedValueDMData>(fExpService.getExecutor(), null) { @Override protected void handleCompleted() { if (!isSuccess()) { wait.waitFinished(getStatus()); } else { if (getData().getFormattedValue().equals("0x1")) { wait.waitFinished(); } else { wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, "Failed evaluating a, expected 0x1 but got " + getData().getFormattedValue(), null)); } } } }); } }); wait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); assertTrue(wait.getMessage(), wait.isOK()); wait.waitReset(); // Now step to change the value of "a" and ask for it again but in the natural format stoppedEvent = SyncUtil.step(1, StepType.STEP_OVER); final IFrameDMContext frameDmc2 = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); fExpService.getExecutor().submit(new Runnable() { public void run() { // First create the var object and all its children IExpressionDMContext exprDmc = fExpService.createExpression(frameDmc2, "a"); // trigger the var-update in the last format (hex) // then request the actual value in natural which should not be taken from the cache wait.increment(); fExpService.getFormattedExpressionValue( fExpService.getFormattedValueContext(exprDmc, IFormattedValues.NATURAL_FORMAT), new DataRequestMonitor<FormattedValueDMData>(fExpService.getExecutor(), null) { @Override protected void handleCompleted() { if (!isSuccess()) { wait.waitFinished(getStatus()); } else { if (getData().getFormattedValue().equals("1.22")) { wait.waitFinished(); } else { wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, "Failed evaluating natural format, expected 1.22 but got " + getData().getFormattedValue(), null)); } } } }); } }); wait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); assertTrue(wait.getMessage(), wait.isOK()); wait.waitReset(); } /** * var-update will not show a change if eval-expression is the same * in the current format. This is a problem for us because we don't * know if another format changed: * * int main() { * struct { * double d; * } z; * * z.d = 1.0; * z.d = 1.22; * } * * If a is displayed in anything but natural, both values of a are the same * and we won't know it changed in the natural format. * This test uses a child to increase the value of the test. * Also, it avoids the cache saving us since we start with the 1.0 value * which is the same in natural and decimal */ @Test public void testUpdateIssue2() throws Throwable { SyncUtil.runToLocation("testUpdateIssue2"); MIStoppedEvent stoppedEvent = SyncUtil.step(1, StepType.STEP_OVER); final IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); final AsyncCompletionWaitor wait = new AsyncCompletionWaitor(); fExpService.getExecutor().submit(new Runnable() { public void run() { IExpressionDMContext parentDmc = fExpService.createExpression(frameDmc, "z"); fExpService.getSubExpressions( parentDmc, new DataRequestMonitor<IExpressionDMContext[]>(fExpService.getExecutor(), null) { @Override protected void handleCompleted() { if (!isSuccess()) { wait.waitFinished(getStatus()); } else if (getData().length != 1) { wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, "Failed getting children; expecting 1 got " + getData().length, null)); } else { // check that we have the proper value // This will cache the value 1 in the natural format cache final String valueStr = "1"; globalExpressionCtx1 = getData()[0]; wait.increment(); fExpService.getFormattedExpressionValue( fExpService.getFormattedValueContext(globalExpressionCtx1, IFormattedValues.NATURAL_FORMAT), new DataRequestMonitor<FormattedValueDMData>(fExpService.getExecutor(), null) { @Override protected void handleCompleted() { if (!isSuccess()) { wait.waitFinished(getStatus()); } else if (getData().getFormattedValue().equals(valueStr)) { wait.waitFinished(); } else { wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, "Failed evaluating " + globalExpressionCtx1.getExpression() + ", got " + getData().getFormattedValue() + " instead of " + valueStr, null)); } } }); // ask for decimal to set the format to decimal wait.increment(); fExpService.getFormattedExpressionValue( fExpService.getFormattedValueContext(globalExpressionCtx1, IFormattedValues.DECIMAL_FORMAT), new DataRequestMonitor<FormattedValueDMData>(fExpService.getExecutor(), null) { @Override protected void handleCompleted() { if (!isSuccess()) { wait.waitFinished(getStatus()); } else { if (getData().getFormattedValue().equals(valueStr)) { wait.waitFinished(); } else { wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, "Failed evaluating " + globalExpressionCtx1.getExpression() + ", got " + getData().getFormattedValue() + " instead of " + valueStr, null)); } } } }); } } }); } }); wait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); assertTrue(wait.getMessage(), wait.isOK()); wait.waitReset(); // Now step to change the value of "a" in natural but it remains the same in decimal SyncUtil.step(1, StepType.STEP_OVER); fExpService.getExecutor().submit(new Runnable() { public void run() { // trigger the var-update in the last format (decimal) // then request the actual value in natural which should not be taken from the cache wait.increment(); fExpService.getFormattedExpressionValue( fExpService.getFormattedValueContext(globalExpressionCtx1, IFormattedValues.NATURAL_FORMAT), new DataRequestMonitor<FormattedValueDMData>(fExpService.getExecutor(), null) { @Override protected void handleCompleted() { if (!isSuccess()) { wait.waitFinished(getStatus()); } else { if (getData().getFormattedValue().equals("1.22")) { wait.waitFinished(); } else { wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, "Failed evaluating natural format, expected 1.22 but got " + getData().getFormattedValue(), null)); } } } }); } }); wait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); assertTrue(wait.getMessage(), wait.isOK()); wait.waitReset(); } /** * This test verifies the state handling of a child variable object * to make sure that our locking scheme works even though we must deal * with an update call, internally */ @Test public void testConcurrentReadAndUpdateChild() throws Throwable { SyncUtil.runToLocation("testConcurrentReadAndUpdateChild"); MIStoppedEvent stoppedEvent = SyncUtil.step(1, StepType.STEP_OVER); final IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); final AsyncCompletionWaitor wait = new AsyncCompletionWaitor(); // Ask for one value to create the var object fExpService.getExecutor().submit(new Runnable() { public void run() { // First create the var object and all its children IExpressionDMContext parentDmc = fExpService.createExpression(frameDmc, "z"); wait.increment(); fExpService.getSubExpressions( parentDmc, new DataRequestMonitor<IExpressionDMContext[]>(fExpService.getExecutor(), null) { @Override protected void handleCompleted() { if (!isSuccess()) { wait.waitFinished(getStatus()); } else if (getData().length != 1) { wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, "Failed getting children; expecting 1 got " + getData().length, null)); } else { // now get the value of the child final String valueStr = "01"; globalExpressionCtx1 = getData()[0]; fExpService.getFormattedExpressionValue( fExpService.getFormattedValueContext(globalExpressionCtx1, IFormattedValues.OCTAL_FORMAT), new DataRequestMonitor<FormattedValueDMData>(fExpService.getExecutor(), null) { @Override protected void handleCompleted() { if (!isSuccess()) { wait.waitFinished(getStatus()); } else if (getData().getFormattedValue().equals(valueStr)) { wait.waitFinished(); } else { wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, "Failed evaluating " + globalExpressionCtx1.getExpression() + ", got " + getData().getFormattedValue() + " instead of " + valueStr, null)); } } }); } } }); } }); wait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); assertTrue(wait.getMessage(), wait.isOK()); wait.waitReset(); // Now do two reads in two different formats // We need to make sure that the locking properly works although we are calling // the internal update method, which does affect the state of the object fExpService.getExecutor().submit(new Runnable() { public void run() { wait.increment(); fExpService.getFormattedExpressionValue( fExpService.getFormattedValueContext(globalExpressionCtx1, IFormattedValues.BINARY_FORMAT), new DataRequestMonitor<FormattedValueDMData>(fExpService.getExecutor(), null) { @Override protected void handleCompleted() { final String valueStr = "1"; if (!isSuccess()) { wait.waitFinished(getStatus()); } else if (getData().getFormattedValue().equals(valueStr)) { wait.waitFinished(); } else { wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, "Failed evaluating " + globalExpressionCtx1.getExpression() + ", got " + getData().getFormattedValue() + " instead of " + valueStr, null)); } } }); wait.increment(); fExpService.getFormattedExpressionValue( fExpService.getFormattedValueContext(globalExpressionCtx1, IFormattedValues.HEX_FORMAT), new DataRequestMonitor<FormattedValueDMData>(fExpService.getExecutor(), null) { @Override protected void handleCompleted() { final String valueStr = "0x1"; if (!isSuccess()) { wait.waitFinished(getStatus()); } else if (getData().getFormattedValue().equals(valueStr)) { wait.waitFinished(); } else { wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, "Failed evaluating " + globalExpressionCtx1.getExpression() + ", got " + getData().getFormattedValue() + " instead of " + valueStr, null)); } } }); } }); wait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); assertTrue(wait.getMessage(), wait.isOK()); wait.waitReset(); } /** * This test verifies some of the logic of dealing with out-of-scope variables. * This particular scenario is that we create a parent with a child and then * have them go out of scope. Then we request the child which will update the parent * and mark it as out-of-scope and recreate the child. The parent is not re-created. * We then ask twice for the parent which is already known to be out-of-scope and we need * to make sure that the parent is re-created once and only once. * We had a bug where we would enter an infinite loop in this case. */ @Test public void testConcurrentUpdateOutOfScopeChildThenParent() throws Throwable { SyncUtil.runToLocation("testConcurrentUpdateOutOfScopeChildThenParent"); MIStoppedEvent stoppedEvent = SyncUtil.step(2, StepType.STEP_INTO); final IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); final AsyncCompletionWaitor wait = new AsyncCompletionWaitor(); fExpService.getExecutor().submit(new Runnable() { public void run() { // First create the var object and its child globalExpressionCtx1 = fExpService.createExpression(frameDmc, "z"); wait.increment(); fExpService.getSubExpressions( globalExpressionCtx1, new DataRequestMonitor<IExpressionDMContext[]>(fExpService.getExecutor(), null) { @Override protected void handleCompleted() { if (!isSuccess()) { wait.waitFinished(getStatus()); } else if (getData().length != 1) { wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, "Failed getting children; expecting 1 got " + getData().length, null)); } else { // now get the value of the child final String valueStr = "1"; globalExpressionCtx2 = getData()[0]; fExpService.getFormattedExpressionValue( fExpService.getFormattedValueContext(globalExpressionCtx2, IFormattedValues.NATURAL_FORMAT), new DataRequestMonitor<FormattedValueDMData>(fExpService.getExecutor(), null) { @Override protected void handleCompleted() { if (!isSuccess()) { wait.waitFinished(getStatus()); } else if (getData().getFormattedValue().equals(valueStr)) { wait.waitFinished(); } else { wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, "Failed evaluating " + globalExpressionCtx2.getExpression() + ", got " + getData().getFormattedValue() + " instead of " + valueStr, null)); } } }); } } }); } }); wait.waitUntilDone(TestsPlugin.massageTimeout(5000)); assertTrue(wait.getMessage(), wait.isOK()); wait.waitReset(); SyncUtil.step(StepType.STEP_RETURN); stoppedEvent = SyncUtil.step(2, StepType.STEP_INTO); // Now step to another method to make the previous variable objects out-of-scope // then first request the child and then the parent. We want to test this order fExpService.getExecutor().submit(new Runnable() { public void run() { wait.increment(); fExpService.getFormattedExpressionValue( fExpService.getFormattedValueContext(globalExpressionCtx2, IFormattedValues.NATURAL_FORMAT), new DataRequestMonitor<FormattedValueDMData>(fExpService.getExecutor(), null) { @Override protected void handleCompleted() { final String valueStr = "2"; if (!isSuccess()) { wait.waitFinished(getStatus()); } else if (getData().getFormattedValue().equals(valueStr)) { wait.waitFinished(); } else { wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, "Failed evaluating " + globalExpressionCtx2.getExpression() + ", got " + getData().getFormattedValue() + " instead of " + valueStr, null)); } } }); } }); wait.waitUntilDone(TestsPlugin.massageTimeout(5000)); assertTrue(wait.getMessage(), wait.isOK()); wait.waitReset(); fExpService.getExecutor().submit(new Runnable() { public void run() { wait.increment(); fExpService.getFormattedExpressionValue( fExpService.getFormattedValueContext(globalExpressionCtx1, IFormattedValues.NATURAL_FORMAT), new DataRequestMonitor<FormattedValueDMData>(fExpService.getExecutor(), null) { @Override protected void handleCompleted() { final String valueStr = "{...}"; if (!isSuccess()) { wait.waitFinished(getStatus()); } else if (getData().getFormattedValue().equals(valueStr)) { wait.waitFinished(); } else { wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, "Failed evaluating " + globalExpressionCtx1.getExpression() + ", got " + getData().getFormattedValue() + " instead of " + valueStr, null)); } } }); // Ask a second time but in a different format, to avoid the cache wait.increment(); fExpService.getFormattedExpressionValue( fExpService.getFormattedValueContext(globalExpressionCtx1, IFormattedValues.DECIMAL_FORMAT), new DataRequestMonitor<FormattedValueDMData>(fExpService.getExecutor(), null) { @Override protected void handleCompleted() { final String valueStr = "{...}"; if (!isSuccess()) { wait.waitFinished(getStatus()); } else if (getData().getFormattedValue().equals(valueStr)) { wait.waitFinished(); } else { wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, "Failed evaluating " + globalExpressionCtx1.getExpression() + ", got " + getData().getFormattedValue() + " instead of " + valueStr, null)); } } }); } }); wait.waitUntilDone(TestsPlugin.massageTimeout(5000)); assertTrue(wait.getMessage(), wait.isOK()); wait.waitReset(); //TODO although this test passes, the variable z is created twice, without being // deleted in GDB. We should fix this } /** * This test verifies that we properly update a pointer and its child since they can both * change and be reported by var-update */ @Test public void testUpdateOfPointer() throws Throwable { SyncUtil.runToLocation("testUpdateOfPointer"); MIStoppedEvent stoppedEvent = SyncUtil.step(3, StepType.STEP_OVER); final IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); final String firstValue = "1"; final String secondValue = "2"; final String thirdValue = "3"; final AsyncCompletionWaitor wait = new AsyncCompletionWaitor(); fExpService.getExecutor().submit(new Runnable() { public void run() { IExpressionDMContext parentDmc = fExpService.createExpression(frameDmc, "z"); fExpService.getSubExpressions( parentDmc, new DataRequestMonitor<IExpressionDMContext[]>(fExpService.getExecutor(), null) { @Override protected void handleCompleted() { if (!isSuccess()) { wait.waitFinished(getStatus()); } else if (getData().length != 2) { wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, "Failed getting children; expecting 2 got " + getData().length, null)); } else { // check that we have the proper value for both children globalExpressionCtx1 = getData()[0]; globalExpressionCtx2 = getData()[1]; // Get the value of the first child wait.increment(); fExpService.getFormattedExpressionValue( fExpService.getFormattedValueContext(globalExpressionCtx1, IFormattedValues.NATURAL_FORMAT), new DataRequestMonitor<FormattedValueDMData>(fExpService.getExecutor(), null) { @Override protected void handleCompleted() { if (!isSuccess()) { wait.waitFinished(getStatus()); } else if (getData().getFormattedValue().equals(firstValue)) { wait.waitFinished(); } else { wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, "Failed evaluating " + globalExpressionCtx1.getExpression() + ", got " + getData().getFormattedValue() + " instead of " + firstValue, null)); } } }); // Get the value of the second child wait.increment(); fExpService.getFormattedExpressionValue( fExpService.getFormattedValueContext(globalExpressionCtx2, IFormattedValues.NATURAL_FORMAT), new DataRequestMonitor<FormattedValueDMData>(fExpService.getExecutor(), null) { @Override protected void handleCompleted() { if (!isSuccess()) { wait.waitFinished(getStatus()); } else { wait.setReturnInfo(getData().getFormattedValue()); wait.waitFinished(); } } }); } } }); } }); wait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); assertTrue(wait.getMessage(), wait.isOK()); final String pointerValue = (String)wait.getReturnInfo(); wait.waitReset(); fExpService.getExecutor().submit(new Runnable() { public void run() { // also get the child of the pointer fExpService.getSubExpressions( globalExpressionCtx2, new DataRequestMonitor<IExpressionDMContext[]>(fExpService.getExecutor(), null) { @Override protected void handleCompleted() { if (!isSuccess()) { wait.waitFinished(getStatus()); } else if (getData().length != 1) { wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, "Failed getting children; expecting 1 got " + getData().length, null)); } else { // Get the value of the child of the pointer globalExpressionCtx2 = getData()[0]; fExpService.getFormattedExpressionValue( fExpService.getFormattedValueContext(globalExpressionCtx2, IFormattedValues.NATURAL_FORMAT), new DataRequestMonitor<FormattedValueDMData>(fExpService.getExecutor(), null) { @Override protected void handleCompleted() { if (!isSuccess()) { wait.waitFinished(getStatus()); } else if (getData().getFormattedValue().equals(firstValue)) { wait.waitFinished(); } else { wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, "Failed evaluating " + globalExpressionCtx2.getExpression() + ", got " + getData().getFormattedValue() + " instead of " + firstValue, null)); } } }); } } }); } }); wait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); assertTrue(wait.getMessage(), wait.isOK()); wait.waitReset(); // Now step to change the values of all the children stoppedEvent = SyncUtil.step(2, StepType.STEP_OVER); final IFrameDMContext frameDmc2 = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); fExpService.getExecutor().submit(new Runnable() { public void run() { IExpressionDMContext parentDmc = fExpService.createExpression(frameDmc2, "z"); fExpService.getSubExpressions( parentDmc, new DataRequestMonitor<IExpressionDMContext[]>(fExpService.getExecutor(), null) { @Override protected void handleCompleted() { if (!isSuccess()) { wait.waitFinished(getStatus()); } else if (getData().length != 2) { wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, "Failed getting children; expecting 2 got " + getData().length, null)); } else { // check that we have the proper value for both children globalExpressionCtx1 = getData()[0]; globalExpressionCtx2 = getData()[1]; // Get the value of the first child wait.increment(); fExpService.getFormattedExpressionValue( fExpService.getFormattedValueContext(globalExpressionCtx1, IFormattedValues.NATURAL_FORMAT), new DataRequestMonitor<FormattedValueDMData>(fExpService.getExecutor(), null) { @Override protected void handleCompleted() { if (!isSuccess()) { wait.waitFinished(getStatus()); } else if (getData().getFormattedValue().equals(secondValue)) { wait.waitFinished(); } else { wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, "Failed evaluating " + globalExpressionCtx1.getExpression() + ", got " + getData().getFormattedValue() + " instead of " + secondValue, null)); } } }); // Get the value of the second child wait.increment(); fExpService.getFormattedExpressionValue( fExpService.getFormattedValueContext(globalExpressionCtx2, IFormattedValues.NATURAL_FORMAT), new DataRequestMonitor<FormattedValueDMData>(fExpService.getExecutor(), null) { @Override protected void handleCompleted() { if (!isSuccess()) { wait.waitFinished(getStatus()); } else if (!getData().getFormattedValue().equals(pointerValue)) { // The value should have changed wait.waitFinished(); } else { wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, "Failed evaluating " + globalExpressionCtx2.getExpression() + ", got " + getData().getFormattedValue() + " instead of some other value", null)); } } }); } } }); } }); wait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); assertTrue(wait.getMessage(), wait.isOK()); wait.waitReset(); fExpService.getExecutor().submit(new Runnable() { public void run() { // also get the child of the pointer fExpService.getSubExpressions( globalExpressionCtx2, new DataRequestMonitor<IExpressionDMContext[]>(fExpService.getExecutor(), null) { @Override protected void handleCompleted() { if (!isSuccess()) { wait.waitFinished(getStatus()); } else if (getData().length != 1) { wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, "Failed getting children; expecting 1 got " + getData().length, null)); } else { // Get the value of the child of the pointer globalExpressionCtx2 = getData()[0]; fExpService.getFormattedExpressionValue( fExpService.getFormattedValueContext(globalExpressionCtx2, IFormattedValues.NATURAL_FORMAT), new DataRequestMonitor<FormattedValueDMData>(fExpService.getExecutor(), null) { @Override protected void handleCompleted() { if (!isSuccess()) { wait.waitFinished(getStatus()); } else if (getData().getFormattedValue().equals(thirdValue)) { wait.waitFinished(); } else { wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, "Failed evaluating " + globalExpressionCtx2.getExpression() + ", got " + getData().getFormattedValue() + " instead of " + thirdValue, null)); } } }); } } }); } }); wait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); assertTrue(wait.getMessage(), wait.isOK()); wait.waitReset(); } /** * This test verifies that we properly return if we can write to different expressions */ @Test public void testCanWrite() throws Throwable { MIStoppedEvent stoppedEvent = SyncUtil.runToLocation("testCanWrite"); final IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); final AsyncCompletionWaitor wait = new AsyncCompletionWaitor(); fExpService.getExecutor().submit(new Runnable() { public void run() { final int exprCount = 5; final IExpressionDMContext dmcs[] = new IExpressionDMContext[exprCount]; final boolean expectedValues[] = new boolean[exprCount]; int exprIndex = 0; dmcs[exprIndex] = fExpService.createExpression(frameDmc, "a"); expectedValues[exprIndex] = true; exprIndex++; dmcs[exprIndex] = fExpService.createExpression(frameDmc, "b"); expectedValues[exprIndex] = true; exprIndex++; dmcs[exprIndex] = fExpService.createExpression(frameDmc, "c"); expectedValues[exprIndex] = false; exprIndex++; dmcs[exprIndex] = fExpService.createExpression(frameDmc, "d"); expectedValues[exprIndex] = false; exprIndex++; dmcs[exprIndex] = fExpService.createExpression(frameDmc, "d[1]"); expectedValues[exprIndex] = true; exprIndex++; for (int index = 0; index < exprCount; index++) { final int finalIndex = index; wait.increment(); fExpService.canWriteExpression( dmcs[finalIndex], new DataRequestMonitor<Boolean>(fExpService.getExecutor(), null) { @Override protected void handleCompleted() { if (!isSuccess()) { wait.waitFinished(getStatus()); } else if (getData() == expectedValues[finalIndex]) { wait.waitFinished(); } else { wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, "Failed establishing proper canWrite for " + dmcs[finalIndex].getExpression() + ", got " + getData() + " instead of " + expectedValues[finalIndex], null)); } } }); } } }); wait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); assertTrue(wait.getMessage(), wait.isOK()); wait.waitReset(); } /** * This test verifies that we properly return if we can write to an expression * that is an L-Value or a Constant */ @Test public void testCanWriteLValue() throws Throwable { MIStoppedEvent stoppedEvent = SyncUtil.runToLocation("testCanWrite"); // Re-use test final IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); final AsyncCompletionWaitor wait = new AsyncCompletionWaitor(); fExpService.getExecutor().submit(new Runnable() { public void run() { final int exprCount = 2; final IExpressionDMContext dmcs[] = new IExpressionDMContext[exprCount]; final boolean expectedValues[] = new boolean[exprCount]; int exprIndex = 0; dmcs[exprIndex] = fExpService.createExpression(frameDmc, "&a"); expectedValues[exprIndex] = false; exprIndex++; dmcs[exprIndex] = fExpService.createExpression(frameDmc, "1"); expectedValues[exprIndex] = false; exprIndex++; for (int index = 0; index < exprCount; index++) { final int finalIndex = index; wait.increment(); fExpService.canWriteExpression( dmcs[finalIndex], new DataRequestMonitor<Boolean>(fExpService.getExecutor(), null) { @Override protected void handleCompleted() { if (!isSuccess()) { wait.waitFinished(getStatus()); } else if (getData() == expectedValues[finalIndex]) { wait.waitFinished(); } else { wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, "Failed establishing proper canWrite for " + dmcs[finalIndex].getExpression() + ", got " + getData() + " instead of " + expectedValues[finalIndex], null)); } } }); } } }); wait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); assertTrue(wait.getMessage(), wait.isOK()); wait.waitReset(); } /** * Executes a group of sub-tests. * * @param tests * A Map in which the key is an expression to evaluate and the * value is an array of expected values, one for each of the * formats supported by the Expressions service (hex, octal, * binary, decimal, natural, details). * @param exact * Indicates whether the natural and details format should * require an exact match to the expected value, or whether the * comparison should match only up to the number of characters * provided in the expected value. Where this is used is in * expressions that involve floating point calculation. Such * calculations are not exact (even when you'd think they should * be) and these tests cannot predict what exactly the result * will be. When this param is false, then we consider it a match * if, e.g., the gdb expression resolves to "1.23456789", but the * caller only supplied "1.2345". */ private void executeExpressionSubTests(final Map<String, String[]> tests, final boolean exact, IDMContext dmc) throws Throwable { // Now evaluate each of the above expressions and compare the actual // value against // the expected value. for (final String expressionToEvaluate : tests.keySet()) { // Get an IExpressionDMContext object representing the expression to // be evaluated. final IExpressionDMContext exprDMC = SyncUtil.createExpression(dmc, expressionToEvaluate); final AsyncCompletionWaitor wait = new AsyncCompletionWaitor(); // Get the list of available format IDs for this expression and for // each one, // get the value of the expression fExpService.getExecutor().submit(new Runnable() { public void run() { fExpService.getAvailableFormats(exprDMC, new DataRequestMonitor<String[]>( fExpService.getExecutor(), null) { @Override protected void handleCompleted() { if (!isSuccess()) { wait.waitFinished(getStatus()); } else { final String[] formatIds = getData(); // Now run the current sub-test using each of // the formats available for the type of // the expression in the sub-test. for (final String formatId : formatIds) { // Get a FormattedValueCMContext object for // the expression-formatID pair. final FormattedValueDMContext valueDmc = fExpService.getFormattedValueContext( exprDMC, formatId); // Increment the number of completed // requests to wait for, since we will send // multiple concurrent requests wait.increment(); // Evaluate the expression represented by // the FormattedValueDMContext object // This actually evaluates the expression. fExpService.getFormattedExpressionValue(valueDmc, new DataRequestMonitor<FormattedValueDMData>(fExpService.getExecutor(), null) { @Override protected void handleCompleted() { if (!isSuccess()) { wait.waitFinished(getStatus()); } else { // Get the // FormattedValueDMData // object from the waiter. FormattedValueDMData exprValueDMData = getData(); final String[] expectedValues = tests.get(expressionToEvaluate); // Check the value of the expression for correctness. String actualValue = exprValueDMData.getFormattedValue(); String expectedValue; if (formatId.equals(IFormattedValues.HEX_FORMAT)) expectedValue = expectedValues[0]; else if (formatId.equals(IFormattedValues.OCTAL_FORMAT)) expectedValue = expectedValues[1]; else if (formatId.equals(IFormattedValues.BINARY_FORMAT)) expectedValue = expectedValues[2]; else if (formatId.equals(IFormattedValues.DECIMAL_FORMAT)) expectedValue = expectedValues[3]; else if (formatId.equals(IFormattedValues.NATURAL_FORMAT)) expectedValue = expectedValues[4]; else if (formatId.equals(MIExpressions.DETAILS_FORMAT)) expectedValue = expectedValues[5]; else expectedValue = "[Unrecognized format ID: " + formatId + "]"; if ((exact == false) && (formatId.equals(IFormattedValues.NATURAL_FORMAT) || formatId.equals(MIExpressions.DETAILS_FORMAT)) && (expectedValue.length() < actualValue.length())) { actualValue = actualValue.substring(0, expectedValue.length()); } if (actualValue.equalsIgnoreCase(expectedValue)) { wait.waitFinished(); } else { String errorMsg = "Failed to correctly evalutate '" + expressionToEvaluate + "': expected '" + expectedValue + "', got '" + actualValue + "'"; wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, errorMsg, null)); } } } }); } } } }); } }); wait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); assertTrue(wait.getMessage(), wait.isOK()); assertTrue("ExprChangedEvent problem: expected 0, received " + getExprChangedCount(), getExprChangedCount() == 0); } } private void executeExpressionSubTests(final Map<String, String[]> tests, IDMContext dmc) throws Throwable { executeExpressionSubTests(tests, true, dmc); } private boolean addressesEqual(IExpressionDMAddress addrToTest, String addrStr, int size) { IAddress addr; if (addrStr.length() <= 10) { addr = new Addr32(addrStr); } else { addr = new Addr64(addrStr); } return addrToTest.getAddress().equals(addr) && addrToTest.getSize() == size; } private void checkAddressData(final IExpressionDMContext dmc, String actualAddrStr, int actualAddrSize) throws Throwable { final AsyncCompletionWaitor wait = new AsyncCompletionWaitor(); fExpService.getExecutor().submit(new Runnable() { public void run() { fExpService.getExpressionAddressData(dmc, new DataRequestMonitor<IExpressionDMAddress>(fExpService .getExecutor(), null) { @Override protected void handleCompleted() { if (isSuccess()) { wait.setReturnInfo(getData()); } wait.waitFinished(getStatus()); } }); } }); wait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); assertTrue(wait.getMessage(), wait.isOK()); IExpressionDMAddress addr = (IExpressionDMAddress)wait.getReturnInfo(); assertTrue("Unable to get address", addr != null); if (addr != null) { assertTrue("Received wrong address of " + addr.toString() + " instead of (" + actualAddrStr + ", " + actualAddrSize + ")", addressesEqual(addr, actualAddrStr, actualAddrSize)); } } private void doTestChildren(MIStoppedEvent stoppedEvent) throws Throwable { final IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); final IExpressionDMContext exprDMC = SyncUtil.createExpression(frameDmc, "f"); IExpressionDMContext[] children = getChildren(exprDMC, new String[] {"bar", "bar2", "a", "b", "c"}); // f.bar IExpressionDMContext[] children1 = getChildren(children[0], new String[] {"d", "e"}); // f.bar.d getChildren(children1[0], new String[0]); // f.bar.e IExpressionDMContext[] children2 = getChildren(children1[1], new String[] {"e[0]", "e[1]"}); // f.bar.e[0] getChildren(children2[0], new String[0]); // f.bar.e[1] getChildren(children2[1], new String[0]); // f.bar2 children1 = getChildren(children[1], new String[] {"f", "g"}); // f.bar2.f getChildren(children1[0], new String[0]); // f.bar2.g children2 = getChildren(children1[1], new String[] {"g[0]", "g[1]"}); // f.bar2.g[0] getChildren(children2[0], new String[0]); // f.bar2.g[1] getChildren(children2[1], new String[0]); // f.a children1 = getChildren(children[2], new String[] {"a[0]", "a[1]"}); // f.a[0] getChildren(children1[0], new String[0]); // f.a[1] getChildren(children1[1], new String[0]); // f.b children1 = getChildren(children[3], new String[] {"d", "e"}); // f.b.d getChildren(children1[0], new String[0]); // f.b.e children2 = getChildren(children1[1], new String[] {"e[0]", "e[1]"}); // f.b.e[0] getChildren(children2[0], new String[0]); // f.b.e[1] getChildren(children2[1], new String[0]); // f.c getChildren(children[4], new String[0]); assertTrue("ExprChangedEvent problem: expected 0, received " + getExprChangedCount(), getExprChangedCount() == 0); } private IExpressionDMContext[] getChildren( final IExpressionDMContext parentDmc, String[] expectedValues) throws Throwable { final AsyncCompletionWaitor wait = new AsyncCompletionWaitor(); fExpService.getExecutor().submit(new Runnable() { public void run() { fExpService.getSubExpressions(parentDmc, new DataRequestMonitor<IExpressionDMContext[]>(fExpService.getExecutor(), null) { @Override protected void handleCompleted() { if (isSuccess()) { wait.setReturnInfo(getData()); } wait.waitFinished(getStatus()); } }); } }); wait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); assertTrue(wait.getMessage(), wait.isOK()); IExpressionDMContext[] childDmcs = (IExpressionDMContext[]) wait.getReturnInfo(); String[] childExpressions = new String[childDmcs.length]; MIExpressionDMCAccessor[] childDmcsAccessor = new MIExpressionDMCAccessor[childDmcs.length]; // Convert to a MIExpressionDMCAccessor to be able to call getRelativeExpression // Also convert to String[] to be able to use Arrays.toString() for (int i = 0; i < childExpressions.length; i++) { childDmcsAccessor[i] = new MIExpressionDMCAccessor(childDmcs[i]); childExpressions[i] = childDmcsAccessor[i].getRelativeExpression(); } assertTrue("Expected " + Arrays.toString(expectedValues) + " but got " + Arrays.toString(childExpressions), expectedValues.length == childExpressions.length); for (int i = 0; i < childDmcsAccessor.length; i++) { assertTrue("Expected: " + expectedValues[i] + " got: " + childDmcsAccessor[i].getRelativeExpression(), childDmcsAccessor[i].getRelativeExpression().equals(expectedValues[i])); } return childDmcs; } }