Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: ea62747e1cc1fd97dfb14c7326d99d96f9fb1ee1 (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
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
/*******************************************************************************
 * Copyright (c) 2011, 2015 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	AB		  - Initial implementation of Test cases
 *******************************************************************************/
package org.eclipse.cdt.tests.dsf.gdb.tests;

import static org.junit.Assert.fail;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

import org.eclipse.cdt.debug.core.ICDTLaunchConfigurationConstants;
import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor;
import org.eclipse.cdt.dsf.concurrent.ImmediateRequestMonitor;
import org.eclipse.cdt.dsf.concurrent.Query;
import org.eclipse.cdt.dsf.datamodel.DMContexts;
import org.eclipse.cdt.dsf.debug.service.IProcesses.IProcessDMContext;
import org.eclipse.cdt.dsf.debug.service.IRunControl.IExitedDMEvent;
import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService.ICommandControlShutdownDMEvent;
import org.eclipse.cdt.dsf.gdb.IGdbDebugPreferenceConstants;
import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin;
import org.eclipse.cdt.dsf.gdb.service.IGDBProcesses;
import org.eclipse.cdt.dsf.gdb.service.command.IGDBControl;
import org.eclipse.cdt.dsf.mi.service.IMIContainerDMContext;
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.ServiceEventWaitor;
import org.eclipse.cdt.tests.dsf.gdb.framework.SyncUtil;
import org.eclipse.cdt.tests.dsf.gdb.launching.TestsPlugin;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.preferences.IEclipsePreferences;
import org.eclipse.core.runtime.preferences.InstanceScope;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.osgi.service.prefs.Preferences;


/**
 * Tests that we can perform different operations while the target
 * is running. 
 */
@RunWith(Parameterized.class)
public class OperationsWhileTargetIsRunningTest extends BaseParametrizedTestCase {

	private static final String TIMEOUT_MESSAGE = "Timeout";

	private DsfServicesTracker fServicesTracker;    
	private IGDBProcesses fProcesses;
	private IMIContainerDMContext fContainerDmc;
	private IGDBControl fControl;

	private static final String EXEC_NAME = "TargetAvail.exe";

	private static boolean fgAutoTerminate;

    @BeforeClass
	public static void doBeforeClass() throws Exception {
		// Save the original values of the preferences used in this class
		fgAutoTerminate = Platform.getPreferencesService().getBoolean( 
				GdbPlugin.PLUGIN_ID,
				IGdbDebugPreferenceConstants.PREF_AUTO_TERMINATE_GDB, 
				true,
				null );		
    }

	@Override
	public void doBeforeTest() throws Exception {
		super.doBeforeTest();

		final DsfSession session = getGDBLaunch().getSession();
		
        Runnable runnable = new Runnable() {
            @Override
			public void run() {
            	fServicesTracker = 
            			new DsfServicesTracker(TestsPlugin.getBundleContext(), 
            					session.getId());

            	fProcesses = fServicesTracker.getService(IGDBProcesses.class);
            	fControl = fServicesTracker.getService(IGDBControl.class);
            }
        };
        session.getExecutor().submit(runnable).get();
        
        fContainerDmc = (IMIContainerDMContext)SyncUtil.getContainerContext();

	}


	@Override
	public void doAfterTest() throws Exception {
		super.doAfterTest();

        if (fServicesTracker!=null) fServicesTracker.dispose();

		// Restore the different preferences we might have changed
		IEclipsePreferences node = InstanceScope.INSTANCE.getNode( GdbPlugin.PLUGIN_ID );
		node.putBoolean( IGdbDebugPreferenceConstants.PREF_AUTO_TERMINATE_GDB, fgAutoTerminate );
	}
	
	@Override
	protected void setLaunchAttributes() {
		super.setLaunchAttributes();
		
		setLaunchAttribute(ICDTLaunchConfigurationConstants.ATTR_PROGRAM_NAME, 
				           EXEC_PATH + EXEC_NAME);
	}

    /**
     * Test that the restart operation works properly while the target is running, and
     * with the option to kill GDB after the process terminates, enabled.  
     */
    @Test
    public void restartWhileTargetRunningKillGDB() throws Throwable {
    	// Restart is not supported for a remote session
    	if (isRemoteSession()) {
    		Assert.assertFalse("Restart operation should not be allowed for a remote session",
    				           SyncUtil.canRestart());
    	    return;
    	}

    	// First set the preference to kill GDB (although it should not happen in this test)
    	Preferences node = InstanceScope.INSTANCE.getNode(GdbPlugin.PLUGIN_ID);
    	node.putBoolean(IGdbDebugPreferenceConstants.PREF_AUTO_TERMINATE_GDB, true);

    	// The target is currently stopped.  We resume to get it running
    	// then we do the restart, and confirm we are then stopped on main
    	SyncUtil.resume();
		MIStoppedEvent stoppedEvent = SyncUtil.restart(getGDBLaunch());
		
		String func = stoppedEvent.getFrame().getFunction();
		Assert.assertTrue("Expected to be stopped at main, but is stopped at " + func,
				"main".equals(func));
		
        // Now make sure GDB is still alive
        Assert.assertTrue("GDB should have been still alive", fControl.isActive());
    }
 
    /**
     * Test that the restart operation works properly while the target is running, and
     * with the option to kill GDB after the process terminates, disabled.  
     */
    @Test
    public void restartWhileTargetRunningGDBAlive() throws Throwable {
    	// Restart is not supported for a remote session
    	if (isRemoteSession()) {
    		Assert.assertFalse("Restart operation should not be allowed for a remote session",
    				           SyncUtil.canRestart());
    	    return;
    	}
    	
    	// First set the preference not to kill gdb
    	Preferences node = InstanceScope.INSTANCE.getNode(GdbPlugin.PLUGIN_ID);
    	node.putBoolean(IGdbDebugPreferenceConstants.PREF_AUTO_TERMINATE_GDB, false);

    	// The target is currently stopped.  We resume to get it running
    	// then we do the restart, and confirm we are then stopped on main
    	SyncUtil.resume();
		MIStoppedEvent stoppedEvent = SyncUtil.restart(getGDBLaunch());
		
		String func = stoppedEvent.getFrame().getFunction();
		Assert.assertTrue("Expected to be stopped at main, but is stopped at " + func,
				"main".equals(func));
		
        // Now make sure GDB is still alive
        Assert.assertTrue("GDB should have been still alive", fControl.isActive());
    }
    
    /**
     * Test that the terminate operation works properly while the target is running, and
     * with the option to kill GDB after the process terminates, enabled. 
     */
    @Test
    public void terminateWhileTargetRunningKillGDB() throws Throwable {
    	// First set the preference to kill GDB
    	Preferences node = InstanceScope.INSTANCE.getNode(GdbPlugin.PLUGIN_ID);
    	node.putBoolean(IGdbDebugPreferenceConstants.PREF_AUTO_TERMINATE_GDB, true);

    	// The target is currently stopped.  We resume to get it running
    	// then we terminate, and confirm that we shutdown right away
    	SyncUtil.resume();
    	
        ServiceEventWaitor<ICommandControlShutdownDMEvent> shutdownEventWaitor = new ServiceEventWaitor<ICommandControlShutdownDMEvent>(
        		getGDBLaunch().getSession(),
        		ICommandControlShutdownDMEvent.class);

        // Don't use a query here.  The terminate, because it kills GDB, may not return right away
        // but that is ok because we wait for a shutdown event right after
        Runnable runnable = new Runnable() {
            @Override
			public void run() {
    	        IProcessDMContext processDmc = DMContexts.getAncestorOfType(fContainerDmc, IProcessDMContext.class);
    	    	fProcesses.terminate(processDmc, new ImmediateRequestMonitor());
            }
        };
        fProcesses.getExecutor().execute(runnable);
    		
		// The shutdown must happen quickly, which will confirm that it was
		// our own terminate that did it.  If it take longer, it indicates
		// that the program terminated on its own, which is not what we want.
        shutdownEventWaitor.waitForEvent(TestsPlugin.massageTimeout(500));
        
        // Now make sure GDB is dead
        Assert.assertTrue("GDB should have been terminated", !fControl.isActive());
    }

    /**
     * Test that the terminate operation works properly while the target is running, and
     * with the option to kill GDB after the process terminates, disabled. 
     */
    @Test
    public void terminateWhileTargetRunningKeepGDBAlive() throws Throwable {
    	// First set the preference not to kill gdb
    	Preferences node = InstanceScope.INSTANCE.getNode(GdbPlugin.PLUGIN_ID);
    	node.putBoolean(IGdbDebugPreferenceConstants.PREF_AUTO_TERMINATE_GDB, false);

    	// The target is currently stopped.  We resume to get it running
    	// then we terminate the process, and confirm that there are no more processes
    	SyncUtil.resume();
    	
        ServiceEventWaitor<IExitedDMEvent> exitedEventWaitor = new ServiceEventWaitor<IExitedDMEvent>(
        		getGDBLaunch().getSession(),
        		IExitedDMEvent.class);

    	Query<Object> query = new Query<Object>() {
    		@Override
    		protected void execute(final DataRequestMonitor<Object> rm) {
    	        IProcessDMContext processDmc = DMContexts.getAncestorOfType(fContainerDmc, IProcessDMContext.class);
    	    	fProcesses.terminate(processDmc, rm);
    		}
    	};
    	try {
    		fProcesses.getExecutor().execute(query);
    		query.get(TestsPlugin.massageTimeout(1000), TimeUnit.MILLISECONDS);
    	} catch (InterruptedException e) {
    		fail(e.getMessage());
    	} catch (ExecutionException e) {
    		fail(e.getCause().getMessage());
    	} catch (TimeoutException e) {
    		fail(TIMEOUT_MESSAGE);
    	}
    		
        IExitedDMEvent event = exitedEventWaitor.waitForEvent(TestsPlugin.massageTimeout(500));
        if (!(event.getDMContext() instanceof IMIContainerDMContext)) {
        	// This was the thread exited event, we want the container exited event
            event = exitedEventWaitor.waitForEvent(TestsPlugin.massageTimeout(500));
        }
        
        // Make sure this event shows that the process was terminated
        Assert.assertTrue("Process was not terminated", event.getDMContext() instanceof IMIContainerDMContext);
        IMIContainerDMContext dmc = (IMIContainerDMContext)event.getDMContext();
        Assert.assertTrue("Expected process " + fContainerDmc.getGroupId() + " but got " + dmc.getGroupId(), 
        		          fContainerDmc.getGroupId().equals(dmc.getGroupId()));
        
        // Now make sure GDB is still alive
        Assert.assertTrue("GDB should have been still alive", fControl.isActive());
    }
    
    /**
     * Test that the detach operation works properly while the target is running, and
     * with the option to kill GDB after the process terminates, enabled.  
     */
    @Test
    public void detachWhileTargetRunningKillGDB() throws Throwable {
    	// First set the preference to kill GDB
    	Preferences node = InstanceScope.INSTANCE.getNode(GdbPlugin.PLUGIN_ID);
    	node.putBoolean(IGdbDebugPreferenceConstants.PREF_AUTO_TERMINATE_GDB, true);

    	// The target is currently stopped.  We resume to get it running
    	// then we detach the process, and confirm that we are shutdown
    	SyncUtil.resume();
    	
        ServiceEventWaitor<ICommandControlShutdownDMEvent> shutdownEventWaitor = new ServiceEventWaitor<ICommandControlShutdownDMEvent>(
        		getGDBLaunch().getSession(),
        		ICommandControlShutdownDMEvent.class);

        // Don't use a query here.  Because GDB will be killed, the call to detach may not return right away
        // but that is ok because we wait for a shutdown event right after
        Runnable runnable = new Runnable() {
            @Override
			public void run() {
    	    	fProcesses.detachDebuggerFromProcess(fContainerDmc, new ImmediateRequestMonitor());
            }
        };
        fProcesses.getExecutor().execute(runnable);
      		
		// The shutdown must happen quickly, which will confirm that it was
		// our own terminate that did it.  If it take longer, it indicates
		// that the program terminated on its own, which is not what we want.
        shutdownEventWaitor.waitForEvent(TestsPlugin.massageTimeout(500));
        
        // Now make sure GDB is dead
        Assert.assertTrue("GDB should have been terminated", !fControl.isActive());
    }
    
    /**
     * Test that the detach operation works properly while the target is running, and
     * with the option to kill GDB after the process terminates, disabled.  
     */
    @Test
    public void detachWhileTargetRunningGDBAlive() throws Throwable {
    	// First set the preference not to kill gdb
    	Preferences node = InstanceScope.INSTANCE.getNode(GdbPlugin.PLUGIN_ID);
    	node.putBoolean(IGdbDebugPreferenceConstants.PREF_AUTO_TERMINATE_GDB, false);

    	// The target is currently stopped.  We resume to get it running
    	// then we detach the process, and confirm that we are not longer running
    	SyncUtil.resume();
    	
        ServiceEventWaitor<IExitedDMEvent> exitedEventWaitor = new ServiceEventWaitor<IExitedDMEvent>(
        		getGDBLaunch().getSession(),
        		IExitedDMEvent.class);

    	Query<Object> query = new Query<Object>() {
    		@Override
    		protected void execute(final DataRequestMonitor<Object> rm) {
    	    	fProcesses.detachDebuggerFromProcess(fContainerDmc, rm);
    		}
    	};
    	try {
    		fProcesses.getExecutor().execute(query);
    		query.get(TestsPlugin.massageTimeout(1000), TimeUnit.MILLISECONDS);
    	} catch (InterruptedException e) {
    		fail(e.getMessage());
    	} catch (ExecutionException e) {
    		fail(e.getCause().getMessage());
    	} catch (TimeoutException e) {
    		fail(TIMEOUT_MESSAGE);
    	}
    		
        IExitedDMEvent event = exitedEventWaitor.waitForEvent(TestsPlugin.massageTimeout(500));
        if (!(event.getDMContext() instanceof IMIContainerDMContext)) {
        	// This was the thread exited event, we want the container exited event
            event = exitedEventWaitor.waitForEvent(TestsPlugin.massageTimeout(500));
        }
        
        // Make sure this event shows that the process was detached
        Assert.assertTrue("Process was not detached", event.getDMContext() instanceof IMIContainerDMContext);
        IMIContainerDMContext dmc = (IMIContainerDMContext)event.getDMContext();
        Assert.assertTrue("Expected process " + fContainerDmc.getGroupId() + " but got " + dmc.getGroupId(), 
        		          fContainerDmc.getGroupId().equals(dmc.getGroupId()));
        
        // Now make sure GDB is still alive
        Assert.assertTrue("GDB should have been still alive", fControl.isActive());
    }
}

Back to the top