/******************************************************************************* * Copyright (c) 2011, 2016 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 * Simon Marchi (Ericsson) - Remove a catch that just fails a test. * Simon Marchi (Ericsson) - Disable tests for gdb < 7.2. * Jonah Graham (Kichwa Coders) - Split arguments tests out of LaunchConfigurationAndRestartTest *******************************************************************************/ package org.eclipse.cdt.tests.dsf.gdb.tests; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.endsWith; import static org.hamcrest.Matchers.startsWith; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import java.util.concurrent.TimeUnit; import org.eclipse.cdt.debug.core.ICDTLaunchConfigurationConstants; import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; import org.eclipse.cdt.dsf.concurrent.Query; import org.eclipse.cdt.dsf.debug.service.IExpressions; import org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionDMContext; import org.eclipse.cdt.dsf.debug.service.IFormattedValues.FormattedValueDMData; import org.eclipse.cdt.dsf.mi.service.MIExpressions; import org.eclipse.cdt.dsf.mi.service.command.events.MIStoppedEvent; import org.eclipse.cdt.dsf.service.DsfServicesTracker; import org.eclipse.cdt.dsf.service.DsfSession; import org.eclipse.cdt.tests.dsf.gdb.framework.BaseParametrizedTestCase; import org.eclipse.cdt.tests.dsf.gdb.framework.SyncUtil; import org.eclipse.cdt.tests.dsf.gdb.launching.TestsPlugin; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @RunWith(Parameterized.class) public class CommandLineArgsTest extends BaseParametrizedTestCase { protected static final String EXEC_NAME = "LaunchConfigurationAndRestartTestApp.exe"; private DsfSession fSession; private DsfServicesTracker fServicesTracker; private IExpressions fExpService; @Override public void doBeforeTest() throws Exception { removeTeminatedLaunchesBeforeTest(); setLaunchAttributes(); // Can't run the launch right away because each test needs to first set // ICDTLaunchConfigurationConstants.ATTR_PROGRAM_ARGUMENTS } @Override protected void setLaunchAttributes() { super.setLaunchAttributes(); // Set the binary setLaunchAttribute(ICDTLaunchConfigurationConstants.ATTR_PROGRAM_NAME, EXEC_PATH + EXEC_NAME); } // This method cannot be tagged as @Before, because the launch is not // running yet. We have to call this manually after all the proper // parameters have been set for the launch @Override protected void doLaunch() throws Exception { // perform the launch super.doLaunch(); fSession = getGDBLaunch().getSession(); Runnable runnable = new Runnable() { @Override public void run() { fServicesTracker = new DsfServicesTracker(TestsPlugin.getBundleContext(), fSession.getId()); fExpService = fServicesTracker.getService(IExpressions.class); } }; fSession.getExecutor().submit(runnable).get(); } /** * Convert a string of form 0x123456 "ab\"cd" to ab"cd */ protected String convertDetails(String details) { // check parser assumptions on input format assertThat(details, startsWith("0x")); assertThat(details, containsString(" \"")); assertThat(details, endsWith("\"")); int firstSpace = details.indexOf(' '); boolean lastWasEscape = false; StringBuilder sb = new StringBuilder(); for (int i = firstSpace + 2; i < details.length() - 1; i++) { char c = details.charAt(i); if (lastWasEscape) { switch (c) { case 't': sb.append('\t'); break; case 'r': sb.append('\r'); break; case 'n': sb.append('\n'); break; default: sb.append(c); break; } lastWasEscape = false; } else { if (c == '\\') { lastWasEscape = true; } else { sb.append(c); } } } assertFalse("unexpected trailing backslash (\\)", lastWasEscape); return sb.toString(); } /** * Check that the target program received the arguments as expected * * @param expected * arguments to check, e.g. check expected[0].equals(argv[1]) */ protected void checkArguments(String... expected) throws Throwable { MIStoppedEvent stoppedEvent = getInitialStoppedEvent(); // Check that argc is correct final IExpressionDMContext argcDmc = SyncUtil.createExpression(stoppedEvent.getDMContext(), "argc"); Query<FormattedValueDMData> query = new Query<FormattedValueDMData>() { @Override protected void execute(DataRequestMonitor<FormattedValueDMData> rm) { fExpService.getFormattedExpressionValue( fExpService.getFormattedValueContext(argcDmc, MIExpressions.DETAILS_FORMAT), rm); } }; fExpService.getExecutor().execute(query); FormattedValueDMData value = query.get(TestsPlugin.massageTimeout(500), TimeUnit.MILLISECONDS); assertTrue("Expected " + (1 + expected.length) + " but got " + value.getFormattedValue(), value.getFormattedValue().trim().equals(Integer.toString(1 + expected.length))); // check all argvs are correct for (int i = 0; i < expected.length; i++) { final IExpressionDMContext argvDmc = SyncUtil.createExpression(stoppedEvent.getDMContext(), "argv[" + (i + 1) + "]"); Query<FormattedValueDMData> query2 = new Query<FormattedValueDMData>() { @Override protected void execute(DataRequestMonitor<FormattedValueDMData> rm) { fExpService.getFormattedExpressionValue( fExpService.getFormattedValueContext(argvDmc, MIExpressions.DETAILS_FORMAT), rm); } }; fExpService.getExecutor().execute(query2); FormattedValueDMData value2 = query2.get(TestsPlugin.massageTimeout(500), TimeUnit.MILLISECONDS); String details = value2.getFormattedValue(); String actual = convertDetails(details); assertEquals(expected[i], actual); } } /** * Run the program, setting ATTR_PROGRAM_ARGUMENTS to the attrProgramArgs * and ensuring debugged program receives args for argv (excluding argv[0] * which isn't checked) */ protected void doTest(String attrProgramArgs, String... args) throws Throwable { setLaunchAttribute(ICDTLaunchConfigurationConstants.ATTR_PROGRAM_ARGUMENTS, attrProgramArgs); doLaunch(); checkArguments(args); } /** * This test will tell the launch to set some arguments for the program. We * will then check that the program has the same arguments. */ @Test public void testSettingArguments() throws Throwable { doTest("1 2 3\n4 5 6", "1", "2", "3", "4", "5", "6"); } /** * This test will tell the launch to set some arguments for the program. We * will then check that the program has the same arguments. See bug 381804 */ @Test public void testSettingArgumentsWithSymbols() throws Throwable { // Set a argument with double quotes and spaces, which should be // considered a single argument doTest("--c=\"c < s: 'a' t: 'b'>\"", "--c=c < s: 'a' t: 'b'>"); } /** * This test will tell the launch to set some more arguments for the * program. We will then check that the program has the same arguments. See * bug 474648 */ @Test public void testSettingArgumentsWithSpecialSymbols() throws Throwable { // Test that arguments are parsed correctly: // The string provided by the user is split into arguments on spaces // except for those inside quotation marks, double or single. // Any character within quotation marks or after the backslash character // is treated literally, whilst these special characters have to be // escaped explicitly to be recorded. // All other characters including semicolons, backticks, pipes, dollars // and newlines // must be treated literally. doTest("--abc=\"x;y;z\nsecondline: \"`date`$PS1\"`date | wc`\"", "--abc=x;y;z\nsecondline: `date`$PS1`date | wc`"); } /** * Check combinations of quote characters */ @Test public void testSettingArgumentsWithQuotes() throws Throwable { doTest("\"'\" '\"'", "'", "\""); } /** * Check tab characters */ @Test public void testSettingArgumentsWithTabs() throws Throwable { doTest("\"\t\"\t'\t'", "\t", "\t"); } }