From 1fc777770ed609b7e3084bada24c0833e810a9c1 Mon Sep 17 00:00:00 2001
From: Rene Schneider <rene.schneider@gebit.de>
Date: Wed, 30 Jan 2019 11:18:02 +0100
Subject: [PATCH 1/3] fixes #223: Add console output when waiting for remote to
 connect

---
 .../de/gebit/integrity/runner/DefaultTestRunner.java  | 11 ++++++++---
 1 file changed, 8 insertions(+), 3 deletions(-)

diff --git a/de.gebit.integrity.runner/src/de/gebit/integrity/runner/DefaultTestRunner.java b/de.gebit.integrity.runner/src/de/gebit/integrity/runner/DefaultTestRunner.java
index 0c68b7a7e..321fbf16f 100644
--- a/de.gebit.integrity.runner/src/de/gebit/integrity/runner/DefaultTestRunner.java
+++ b/de.gebit.integrity.runner/src/de/gebit/integrity/runner/DefaultTestRunner.java
@@ -772,6 +772,8 @@ public void run() {
 
 			if (remotingServer != null && tempBlockForRemoting) {
 				try {
+					System.out.println(
+							"WAITING FOR REMOTE CONTROLLER TO CONNECT ON PORT " + remotingServer.getPort() + "...");
 					performanceLogger.executeAndLog(TestRunnerPerformanceLogger.PERFORMANCE_LOG_CATEGORY_RUNNER,
 							"Wait for Remoting",
 							new TestRunnerPerformanceLogger.RunnableWithException<InterruptedException>() {
@@ -864,8 +866,10 @@ protected void initializeParameterizedConstants() {
 				String tempValue = (parameterizedConstantValues != null) ? parameterizedConstantValues.get(tempName)
 						: null;
 				try {
-					defineConstant(tempDefinition, tempValue, (tempDefinition.eContainer() instanceof SuiteDefinition)
-							? ((SuiteDefinition) tempDefinition.eContainer()) : null);
+					defineConstant(tempDefinition, tempValue,
+							(tempDefinition.eContainer() instanceof SuiteDefinition)
+									? ((SuiteDefinition) tempDefinition.eContainer())
+									: null);
 				} catch (ClassNotFoundException | InstantiationException | UnexecutableException exc) {
 					// Cannot happen - parameterized constants aren't evaluated
 				}
@@ -1573,7 +1577,8 @@ protected void defineConstant(final ConstantDefinition aDefinition, Object aValu
 	protected void defineVariable(final VariableOrConstantEntity anEntity, Object anInitialValue,
 			final SuiteDefinition aSuite) {
 		final Object tempInitialValue = (anInitialValue instanceof Variable)
-				? variableManager.get(((Variable) anInitialValue).getName()) : anInitialValue;
+				? variableManager.get(((Variable) anInitialValue).getName())
+				: anInitialValue;
 
 		// We need to send variable updates to forks in the main phase here.
 		boolean tempSendToForks = (!isFork()) && shouldExecuteFixtures();

From ce9747cf2f26f92073b65050a46ad81c38fe35b4 Mon Sep 17 00:00:00 2001
From: Rene Schneider <rene.schneider@gebit.de>
Date: Wed, 30 Jan 2019 11:18:28 +0100
Subject: [PATCH 2/3] fixes #217: Implement "Pause/Abort at first error"
 feature in Eclipse

---
 .../icons/auto_pause_abort_disabled.gif       | Bin 0 -> 577 bytes
 .../icons/auto_pause_abort_on_abort.gif       | Bin 0 -> 532 bytes
 .../icons/auto_pause_abort_on_pause.gif       | Bin 0 -> 552 bytes
 .../views/IntegrityTestRunnerView.java        | 137 ++++++++++++++++--
 4 files changed, 126 insertions(+), 11 deletions(-)
 create mode 100644 de.gebit.integrity.eclipse/icons/auto_pause_abort_disabled.gif
 create mode 100644 de.gebit.integrity.eclipse/icons/auto_pause_abort_on_abort.gif
 create mode 100644 de.gebit.integrity.eclipse/icons/auto_pause_abort_on_pause.gif

diff --git a/de.gebit.integrity.eclipse/icons/auto_pause_abort_disabled.gif b/de.gebit.integrity.eclipse/icons/auto_pause_abort_disabled.gif
new file mode 100644
index 0000000000000000000000000000000000000000..004f4acb6763e9c2d556af75d5b6ffd7531d1bb4
GIT binary patch
literal 577
zcmZ?wbhEHb6krfwc*ekRP*ifisKkH&p#KTU|H~_`ib-D)lfEOR@ZUfDe{j@C|Iq)T
zQ6GcDKZQpAk4^laSNzh{?tg6B|E$9Q?-&06^Y;Ji$^W1C{J&lF|6b$&N4@`FuKNG(
z;Qtd*|1TE&ztZ^sYSaI#&HwLA|9@}J|K~gYpUL@url2}dxFJ-uDO{{GTB1E#q9;yj
z|Dy7<-9{(d4bOI)-kD*4f40+uInEE4hQ2*H^J1^r?dcBpXF1)S>2#sj>GA&Q&(Ewo
zQKkLv`u?ZO^45gQOmGuFtFL&$M(_W`&a;X#|MPSH=jQ%jy7d2w75~A2VQ>S*pDc_F
z3|<U6AZLQ&gn@ltgIAN6wULo^b7z;iw6tN@1ZxXrWs8mp%?8qHYSLyinv6{q)HM`L
zO%~{j%WG=ONjW*XICUE9O33U|a&dQcc6VvklT=aE(%I|j?tc1^mxGw9tgQgA+u74@
zOad3|M1@5d1vxl8J)a6PHrd-)TCsC;-gM{WW}hIyb@t3@uDcU>Y#fvhGEQP=b<os2
Xkifvm#wn`RSir#G&^d#Nk--`ONy@mT

literal 0
HcmV?d00001

diff --git a/de.gebit.integrity.eclipse/icons/auto_pause_abort_on_abort.gif b/de.gebit.integrity.eclipse/icons/auto_pause_abort_on_abort.gif
new file mode 100644
index 0000000000000000000000000000000000000000..8159da870ab58e918b20fb01a9edd66833d93a98
GIT binary patch
literal 532
zcmZ?wbhEHb6krfw_{IPN&-S#x*pT$}z~tv=);(LB@c-qm`%5AoE)Bgk&Hmn@uG%1x
zua~DhT%CGplI`nVQ4hCebjC>jxzcj4ChDw?!g*7z+w;7?ozDMpH1W-@=r>bxzs;TV
zzq#T6rj0l4ZO$snY^$>Tf2r)t*~U9raqmLIp3R<lHYxaWZRxcMeam9CUmqy^a%b*k
zAJ6|03HQSMf2>{pc|!Ns*;DV7XP*dgSQ9RDuu%Q#tng=+cHa|H@6}a2n>+V~Pr&o6
z+^adMC){oBY3bjI4!<#N+6yPIM~P|YQxi@&SX>p8K51e7p{(qMfyHYt|C`~#drT}o
zWEH$nF?wz1dC@!Ih^+3@mX;SLHqQgYFL?$X5Kwr#Xwm29){lJ?F8hX^3r|0$sDIiq
zK+W|e9gyNr7Dfh!I0hY1On~BqfxWOHuBo}DwS|$Xtt*a|t-Fnjo2S2-UqDbuc#4>~
zgrt<TjBK}pqLQ+Ts@m!`YU<5e+B&*=`Ucy#8ydA)SXx;hIACMj<>2Ua;)JtHw}<EX
o^IqQFzJAxP`3H0d1>e0J651Ue@#INlRCmm)*D<kEx*8d*0dc<dr~m)}

literal 0
HcmV?d00001

diff --git a/de.gebit.integrity.eclipse/icons/auto_pause_abort_on_pause.gif b/de.gebit.integrity.eclipse/icons/auto_pause_abort_on_pause.gif
new file mode 100644
index 0000000000000000000000000000000000000000..66e8b6aba49de40dccfc41139c1ecf894dd0739a
GIT binary patch
literal 552
zcmZ?wbhEHb6krfw_{IPN&-S#x*pT$}z~tv=);(LB@c-qm`%5AoE)Bgk&Hmn@uG%1x
zua~DhT%CGplI`nVQ4hCebjC>jxzcj4ChDw?!g*7z+w;7?ozDMpH1W-@=r>bxzs#HY
zr@8vihLty5ZO$61oZptZw?*va5|0ZzGPYG&{=ZcA<!s}fthjffVW*aQU)h*_W<|(T
zAIGyR!<NNrzdlg-<<8v8KA!(05)Sog{rK_W;_CPdrkeXZ<n}a+t_hbpSg3w=R`@eZ
zyYC692YNJqe7bjjdE_G}i#=_!m)EB3Y81VvrN6I3^~bxTKi(WXw>0Ruhs5qC>5HqQ
zchreq6_eiGqWt6e<{!`3pPui%rAlOboz#!#Th1*F+1{x1<ME;&52l}->#?CscyqPn
zk0(oi+@Er0aln>3r5`Uh|9H0Q_#E%G#ey5kC4Rix`s3+}(~Ckk)hPdXf9c2j3rA)L
zuPP8+S1Ng8QS6=>!RsrPH&iO`oZz-^cI4h!VLSR<)Lc*ELW)0G7#SGK8FWA~0g4j_
z_SS~-rskH`7DlGFu5wnk?lvxNp8jTj0YM?*DPrOhl2Xz#vfV1GYU&!AT3XuWN~?97
z^$iS-j7>~T&CGXbwb|O)+dDWq9dhn+bNBEp_de~@<sX<7R2~u#c(yG(A}*>tCN@s9
mD<LpFxjZ#7Hlr&uJ3FU5?^AwPL19r@dFk)J8j@Tr4AuacgAWb>

literal 0
HcmV?d00001

diff --git a/de.gebit.integrity.eclipse/src/de/gebit/integrity/eclipse/views/IntegrityTestRunnerView.java b/de.gebit.integrity.eclipse/src/de/gebit/integrity/eclipse/views/IntegrityTestRunnerView.java
index 009ca923a..88c11e842 100644
--- a/de.gebit.integrity.eclipse/src/de/gebit/integrity/eclipse/views/IntegrityTestRunnerView.java
+++ b/de.gebit.integrity.eclipse/src/de/gebit/integrity/eclipse/views/IntegrityTestRunnerView.java
@@ -559,6 +559,38 @@ public class IntegrityTestRunnerView extends ViewPart {
 	 */
 	private boolean scrollLockActive;
 
+	/**
+	 * The action which toggles between normal mode, pause at first error or abort at first error.
+	 */
+	private Action pauseAbortAtFirstErrorAction;
+
+	/**
+	 * Whether the test run should be paused when the first error is encountered.
+	 */
+	private boolean pauseAtFirstErrorActive;
+
+	/**
+	 * Whether the test run should be aborted when the first error is encountered.
+	 */
+	private boolean abortAtFirstErrorActive;
+
+	/**
+	 * Will be set when the {@link #abortAtFirstErrorActive} flag triggers an abortion of test execution.
+	 */
+	private boolean lastRunWasAutoAborted;
+
+	/**
+	 * The threshold of accepted test failures before the {@link #pauseAtFirstErrorActive} or
+	 * {@link #abortAtFirstErrorActive} triggers an action.
+	 */
+	private int pauseAbortAtFirstErrorFailureThreshold;
+
+	/**
+	 * If the last test run has reported its ending normally. This is used to distinguish between connection losses of
+	 * unexpected nature and such losses that occur because testing has ended.
+	 */
+	private boolean lastRunEndStatusReceived;
+
 	/**
 	 * The last level of node expansion.
 	 */
@@ -589,6 +621,11 @@ public class IntegrityTestRunnerView extends ViewPart {
 	 */
 	private SetList setList;
 
+	/**
+	 * Used to measure the connected time.
+	 */
+	private long connectionTimestamp;
+
 	/**
 	 * The set of breakpoints currently in use.
 	 */
@@ -1501,6 +1538,7 @@ private void fillLocalToolBar(IToolBarManager aManager) {
 		aManager.add(stepIntoAction);
 		aManager.add(stepOverAction);
 		aManager.add(clearBreakpointsAction);
+		aManager.add(pauseAbortAtFirstErrorAction);
 		aManager.add(new Separator());
 		aManager.add(connectToTestRunnerAction);
 	}
@@ -1758,6 +1796,40 @@ public void run() {
 		scrollLockAction.setToolTipText("Toggles the scroll lock setting.");
 		scrollLockAction.setImageDescriptor(Activator.getImageDescriptor("icons/scrolllock.gif"));
 
+		pauseAbortAtFirstErrorAction = new Action("Pause/abort at first test failure", IAction.AS_CHECK_BOX) {
+			@Override
+			public void run() {
+				if (!pauseAtFirstErrorActive && !abortAtFirstErrorActive) {
+					pauseAtFirstErrorActive = true;
+					setChecked(true);
+					setImageDescriptor(Activator.getImageDescriptor("icons/auto_pause_abort_on_pause.gif"));
+					setToolTipText("Will automatically pause test execution on first test failure.");
+				} else if (pauseAtFirstErrorActive) {
+					pauseAtFirstErrorActive = false;
+					abortAtFirstErrorActive = true;
+					setChecked(true);
+					setImageDescriptor(Activator.getImageDescriptor("icons/auto_pause_abort_on_abort.gif"));
+					setToolTipText("Will automatically abort test execution on first test failure.");
+				} else {
+					pauseAtFirstErrorActive = false;
+					abortAtFirstErrorActive = false;
+					setChecked(false);
+					setImageDescriptor(Activator.getImageDescriptor("icons/auto_pause_abort_disabled.gif"));
+					setToolTipText("Automatically pause or abort on first test failure is currently disabled.");
+				}
+
+				if (setList != null) {
+					pauseAbortAtFirstErrorFailureThreshold = setList
+							.getNumberOfEntriesInResultState(SetListEntryResultStates.FAILED)
+							+ setList.getNumberOfEntriesInResultState(SetListEntryResultStates.EXCEPTION);
+				}
+			};
+		};
+		pauseAbortAtFirstErrorAction
+				.setToolTipText("Automatically pause or abort on first test failure is currently disabled.");
+		pauseAbortAtFirstErrorAction
+				.setImageDescriptor(Activator.getImageDescriptor("icons/auto_pause_abort_disabled.gif"));
+
 		updateActionStatus(null);
 	}
 
@@ -1856,9 +1928,19 @@ public void run() {
 							pauseAction.setEnabled(false);
 							stepIntoAction.setEnabled(false);
 							stepOverAction.setEnabled(false);
-							updateStatusRunnable(
-									determineIntermediateTestResultStatusString("Test execution finished (", ")"))
-											.run();
+							if (lastRunWasAutoAborted) {
+								updateStatusRunnable(
+										determineIntermediateTestResultStatusString("Test was aborted on failure (",
+												") after " + DateUtil.convertNanosecondTimespanToHumanReadableFormat(
+														System.nanoTime() - connectionTimestamp, true, false))).run();
+							} else {
+								updateStatusRunnable(
+										determineIntermediateTestResultStatusString("Test execution finished (",
+												") after " + DateUtil.convertNanosecondTimespanToHumanReadableFormat(
+														System.nanoTime() - connectionTimestamp, true, false))).run();
+							}
+							lastRunWasAutoAborted = false;
+							lastRunEndStatusReceived = true;
 							break;
 						default:
 							break;
@@ -2442,6 +2524,8 @@ private void updateSetList(SetList aNewSetList) {
 		setList = aNewSetList;
 		breakpointSet.clear();
 		setListSearch = null;
+		pauseAbortAtFirstErrorFailureThreshold = 0;
+		lastRunWasAutoAborted = false;
 
 		Display.getDefault().asyncExec(new Runnable() {
 			@Override
@@ -2492,11 +2576,6 @@ public boolean isSkippable() {
 
 	private class RemotingListener implements IntegrityRemotingClientListener {
 
-		/**
-		 * Used to measure the connected time.
-		 */
-		private long connectionTimestamp;
-
 		@Override
 		public void onConnectionSuccessful(IntegrityRemotingVersionMessage aRemoteVersion, Endpoint anEndpoint) {
 			// request set list baseline and execution state
@@ -2578,9 +2657,20 @@ public void onConnectionLost(Endpoint anEndpoint) {
 				public void run() {
 					executionProgress.redraw();
 					updateActionStatusRunnable(null).run();
-					updateStatusRunnable(determineIntermediateTestResultStatusString("Test Runner disconnected (",
-							") after " + DateUtil.convertNanosecondTimespanToHumanReadableFormat(
-									System.nanoTime() - connectionTimestamp, true, false))).run();
+					if (lastRunWasAutoAborted) {
+						updateStatusRunnable(
+								determineIntermediateTestResultStatusString("Test was aborted on failure (",
+										") after " + DateUtil.convertNanosecondTimespanToHumanReadableFormat(
+												System.nanoTime() - connectionTimestamp, true, false))).run();
+					} else {
+						if (!lastRunEndStatusReceived) {
+							updateStatusRunnable(
+									determineIntermediateTestResultStatusString("Test Runner disconnected (",
+											") after " + DateUtil.convertNanosecondTimespanToHumanReadableFormat(
+													System.nanoTime() - connectionTimestamp, true, false))).run();
+						}
+					}
+					lastRunEndStatusReceived = false;
 				}
 			});
 
@@ -2623,6 +2713,31 @@ public void onSetListUpdate(final SetListEntry[] someUpdatedEntries, final Integ
 			if (anEntryInExecutionReference != null) {
 				setList.setEntryInExecutionReference(anEntryInExecutionReference);
 			}
+
+			if (pauseAtFirstErrorActive || abortAtFirstErrorActive) {
+				int tempCurrentFailures = setList.getNumberOfEntriesInResultState(SetListEntryResultStates.FAILED)
+						+ setList.getNumberOfEntriesInResultState(SetListEntryResultStates.EXCEPTION);
+				if (tempCurrentFailures > pauseAbortAtFirstErrorFailureThreshold) {
+					pauseAbortAtFirstErrorFailureThreshold = tempCurrentFailures;
+					if (pauseAtFirstErrorActive && isConnected()) {
+						Display.getDefault().asyncExec(new Runnable() {
+							@Override
+							public void run() {
+								pauseAction.run();
+							}
+						});
+					} else if (abortAtFirstErrorActive && isConnected()) {
+						lastRunWasAutoAborted = true;
+						Display.getDefault().asyncExec(new Runnable() {
+							@Override
+							public void run() {
+								shutdownAction.run();
+							}
+						});
+					}
+				}
+			}
+
 			Display.getDefault().asyncExec(new Runnable() {
 				@Override
 				public void run() {

From 02ef90eead800157a8d30fc2f16629c4b41a3ee9 Mon Sep 17 00:00:00 2001
From: Rene Schneider <rene.schneider@gebit.de>
Date: Wed, 30 Jan 2019 12:16:41 +0100
Subject: [PATCH 3/3] fixes #219: Add "auto-repeat" button to Integrity Test
 Runner view

---
 .../eclipse/views/ConnectDialog.java          | 62 +++++++++++++++
 .../views/IntegrityTestRunnerView.java        | 75 +++++++++++++++++--
 2 files changed, 129 insertions(+), 8 deletions(-)
 create mode 100644 de.gebit.integrity.eclipse/src/de/gebit/integrity/eclipse/views/ConnectDialog.java

diff --git a/de.gebit.integrity.eclipse/src/de/gebit/integrity/eclipse/views/ConnectDialog.java b/de.gebit.integrity.eclipse/src/de/gebit/integrity/eclipse/views/ConnectDialog.java
new file mode 100644
index 000000000..bf490c23c
--- /dev/null
+++ b/de.gebit.integrity.eclipse/src/de/gebit/integrity/eclipse/views/ConnectDialog.java
@@ -0,0 +1,62 @@
+/*******************************************************************************
+ * Copyright (c) 2013 Rene Schneider, GEBIT Solutions GmbH 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
+ *******************************************************************************/
+package de.gebit.integrity.eclipse.views;
+
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.jface.dialogs.IInputValidator;
+import org.eclipse.jface.dialogs.InputDialog;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Shell;
+
+/**
+ *
+ *
+ * @author Rene Schneider - initial API and implementation
+ *
+ */
+public class ConnectDialog extends InputDialog {
+
+	/**
+	 * Whether auto-retry was chosen.
+	 */
+	private boolean autoRetryEnabled;
+
+	/**
+	 * @param parentShell
+	 * @param aDialogTitle
+	 * @param aDialogMessage
+	 * @param anInitialValue
+	 * @param aValidator
+	 */
+	public ConnectDialog(Shell aParentShell, String anInitialValue, IInputValidator aValidator) {
+		super(aParentShell, "Connect to test runner", "Please enter the hostname or IP address to connect to",
+				anInitialValue, null);
+	}
+
+	@Override
+	protected void createButtonsForButtonBar(Composite aParent) {
+		super.createButtonsForButtonBar(aParent);
+
+		createButton(aParent, IDialogConstants.RETRY_ID, "OK (retry)", false);
+	}
+
+	@Override
+	protected void buttonPressed(int aButtonId) {
+		if (aButtonId == IDialogConstants.RETRY_ID) {
+			autoRetryEnabled = true;
+			super.buttonPressed(IDialogConstants.OK_ID);
+		} else {
+			super.buttonPressed(aButtonId);
+		}
+	}
+
+	public boolean isAutoRetryEnabled() {
+		return autoRetryEnabled;
+	}
+
+}
diff --git a/de.gebit.integrity.eclipse/src/de/gebit/integrity/eclipse/views/IntegrityTestRunnerView.java b/de.gebit.integrity.eclipse/src/de/gebit/integrity/eclipse/views/IntegrityTestRunnerView.java
index 88c11e842..f5a1373d1 100644
--- a/de.gebit.integrity.eclipse/src/de/gebit/integrity/eclipse/views/IntegrityTestRunnerView.java
+++ b/de.gebit.integrity.eclipse/src/de/gebit/integrity/eclipse/views/IntegrityTestRunnerView.java
@@ -49,7 +49,6 @@
 import org.eclipse.jface.action.MenuManager;
 import org.eclipse.jface.action.Separator;
 import org.eclipse.jface.dialogs.Dialog;
-import org.eclipse.jface.dialogs.InputDialog;
 import org.eclipse.jface.dialogs.MessageDialog;
 import org.eclipse.jface.resource.JFaceResources;
 import org.eclipse.jface.viewers.ArrayContentProvider;
@@ -641,6 +640,11 @@ public class IntegrityTestRunnerView extends ViewPart {
 	 */
 	private ILaunchConfiguration launchConfiguration;
 
+	/**
+	 * The currently active autoconnect thread.
+	 */
+	private AutoConnectThread autoConnectThread;
+
 	/**
 	 * The constructor.
 	 */
@@ -1550,8 +1554,7 @@ private void makeActions() {
 			@Override
 			public void run() {
 				if (client == null || !client.isActive()) {
-					InputDialog tempDialog = new InputDialog(getSite().getShell(), "Connect to test runner",
-							"Please enter the hostname or IP address to connect to", lastHostname, null);
+					ConnectDialog tempDialog = new ConnectDialog(getSite().getShell(), lastHostname, null);
 					if (tempDialog.open() == IStatus.OK && tempDialog.getValue() != null
 							&& tempDialog.getValue().length() > 0) {
 						lastHostname = tempDialog.getValue();
@@ -1569,7 +1572,23 @@ public void run() {
 							}
 							tempHost = tempHost.substring(0, tempHost.indexOf(':'));
 						}
-						connectToTestRunnerAsync(tempHost, tempPort);
+
+						if (tempDialog.isAutoRetryEnabled()) {
+							if (autoConnectThread != null && autoConnectThread.isAlive()) {
+								autoConnectThread.kill();
+								autoConnectThread = null;
+							}
+							if (autoConnectThread == null || !autoConnectThread.isAlive()) {
+								autoConnectThread = new AutoConnectThread(tempHost, tempPort);
+								autoConnectThread.start();
+							}
+						} else {
+							connectToTestRunnerAsync(tempHost, tempPort);
+						}
+					} else {
+						if (autoConnectThread != null && autoConnectThread.isAlive()) {
+							autoConnectThread.kill();
+						}
 					}
 				} else {
 					disconnectFromTestRunner();
@@ -1709,7 +1728,11 @@ public void run() {
 						executeTestAction.setEnabled(false);
 						executeDebugTestAction.setEnabled(false);
 						final ILaunch tempLaunch = launchConfiguration.launch(ILaunchManager.RUN_MODE, null);
-						new AutoConnectThread(tempLaunch).start();
+						if (autoConnectThread != null && autoConnectThread.isAlive()) {
+							autoConnectThread.kill();
+						}
+						autoConnectThread = new AutoConnectThread(tempLaunch);
+						autoConnectThread.start();
 					} catch (CoreException exc) {
 						showException(exc);
 					}
@@ -1727,7 +1750,11 @@ public void run() {
 						executeTestAction.setEnabled(false);
 						executeDebugTestAction.setEnabled(false);
 						final ILaunch tempLaunch = launchConfiguration.launch(ILaunchManager.DEBUG_MODE, null);
-						new AutoConnectThread(tempLaunch).start();
+						if (autoConnectThread != null && autoConnectThread.isAlive()) {
+							autoConnectThread.kill();
+						}
+						autoConnectThread = new AutoConnectThread(tempLaunch);
+						autoConnectThread.start();
 					} catch (CoreException exc) {
 						showException(exc);
 					}
@@ -2854,6 +2881,21 @@ private class AutoConnectThread extends Thread {
 		 */
 		private ILaunch launch;
 
+		/**
+		 * The host to connect to.
+		 */
+		private String host = "localhost";
+
+		/**
+		 * The port to connect to.
+		 */
+		private int port = IntegrityRemotingConstants.DEFAULT_PORT;
+
+		/**
+		 * Used to kill this thread gracefully.
+		 */
+		private boolean killSwitch;
+
 		/**
 		 * Creates a new instance.
 		 */
@@ -2862,15 +2904,28 @@ private class AutoConnectThread extends Thread {
 			launch = aLaunch;
 		}
 
+		/**
+		 * Creates a new instance.
+		 */
+		AutoConnectThread(String aHost, int aPort) {
+			super("Integrity Autoconnect Thread");
+			host = aHost;
+			port = aPort;
+		}
+
+		public void kill() {
+			killSwitch = true;
+		}
+
 		@Override
 		public void run() {
 			boolean tempSuccess = false;
 
-			while (!launch.isTerminated()) {
+			while (!killSwitch && (launch == null || !launch.isTerminated())) {
 				try {
 					if (!tempSuccess && !isConnected()) {
 						// try to connect at least once
-						connectToTestRunner("localhost", IntegrityRemotingConstants.DEFAULT_PORT);
+						connectToTestRunner(host, IntegrityRemotingConstants.DEFAULT_PORT);
 						tempSuccess = true;
 					} else {
 						// Now we'll wait until the launch has terminated
@@ -2893,6 +2948,10 @@ public void run() {
 					// don't care
 				}
 			}
+
+			if (!tempSuccess) {
+				updateStatus("Aborted connection attempts");
+			}
 			executeTestAction.setEnabled(true);
 			executeDebugTestAction.setEnabled(true);
 		};