Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: 2cc8a719f7a5d3eb5d227d8572fa271db0e662eb (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
/*******************************************************************************
 * Copyright (c) 2011, 2016 Ericsson and others.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * 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.Ignore;
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 {
		assumeLocalSession();
		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 = () -> {
			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
	@Ignore
	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");
	}
}

Back to the top