1 /*******************************************************************************
2  * Copyright (c) 2000, 2019 IBM Corporation and others.
3  *
4  * This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License 2.0
6  * which accompanies this distribution, and is available at
7  * https://www.eclipse.org/legal/epl-2.0/
8  *
9  * SPDX-License-Identifier: EPL-2.0
10  *
11  * Contributors:
12  *     IBM Corporation - initial API and implementation
13  *     Sebastian Davids: sdavids@gmx.de bug 26754
14  *******************************************************************************/
15 package org.eclipse.jdt.internal.junit.runner;
16 
17 import java.io.BufferedReader;
18 import java.io.BufferedWriter;
19 import java.io.File;
20 import java.io.FileInputStream;
21 import java.io.IOException;
22 import java.io.InputStreamReader;
23 import java.io.OutputStreamWriter;
24 import java.io.PrintWriter;
25 import java.io.StringWriter;
26 import java.io.UnsupportedEncodingException;
27 import java.net.Socket;
28 import java.util.Vector;
29 
30 import org.eclipse.jdt.internal.junit.runner.junit3.JUnit3TestLoader;
31 
32 /**
33  * A TestRunner that reports results via a socket connection.
34  * See MessageIds for more information about the protocol.
35  */
36 public class RemoteTestRunner implements MessageSender, IVisitsTestTrees {
37 	/**
38 	 * Holder for information for a rerun request
39 	 */
40 	private static class RerunRequest {
41 		String fRerunClassName;
42 		String fRerunTestName;
43 		int fRerunTestId;
44 
RerunRequest(int testId, String className, String testName)45 		public RerunRequest(int testId, String className, String testName) {
46 			fRerunTestId= testId;
47 			fRerunClassName= className;
48 			fRerunTestName= testName;
49 		}
50 
51 	}
52 
53 	public static final String RERAN_FAILURE = "FAILURE"; //$NON-NLS-1$
54 
55 	public static final String RERAN_ERROR = "ERROR"; //$NON-NLS-1$
56 
57 	public static final String RERAN_OK = "OK"; //$NON-NLS-1$
58 
59 	/**
60 	 * The name of the test classes to be executed
61 	 */
62 	private String[] fTestClassNames;
63 	/**
64 	 * The name of the test (argument -test)
65 	 */
66 	private String fTestName;
67 	/**
68 	 * The names of the packages containing tests to run
69 	 */
70 	private String[] fPackageNames;
71 	/**
72 	 * The unique ID of test to run or "" if not available
73 	 */
74 	private String fUniqueId;
75 	/**
76 	 * Tags to be included and excluded in the test run
77 	 */
78 	private String[][] fIncludeExcludeTags= new String[2][];
79 
80 	/**
81 	 * The current test result
82 	 */
83 	private TestExecution fExecution;
84 
85 	/**
86 	 * The client socket.
87 	 */
88 	private Socket fClientSocket;
89 	/**
90 	 * Print writer for sending messages
91 	 */
92 	private PrintWriter fWriter;
93 	/**
94 	 * Reader for incoming messages
95 	 */
96 	private BufferedReader fReader;
97 	/**
98 	 * Host to connect to, default is the localhost
99 	 */
100 	private String fHost= ""; //$NON-NLS-1$
101 	/**
102 	 * Port to connect to.
103 	 */
104 	private int fPort= -1;
105 	/**
106 	 * Is the debug mode enabled?
107 	 */
108 	private boolean fDebugMode= false;
109 	/**
110 	 * Keep the test run server alive after a test run has finished.
111 	 * This allows to rerun tests.
112 	 */
113 	private boolean fKeepAlive= false;
114 	/**
115 	 * Has the server been stopped
116 	 */
117 	private boolean fStopped= false;
118 	/**
119 	 * Queue of rerun requests.
120 	 */
121 	private Vector<RerunRequest> fRerunRequests= new Vector<RerunRequest>(10);
122 	/**
123 	 * Thread reading from the socket
124 	 */
125 	private ReaderThread fReaderThread;
126 
127 	private String fRerunTest;
128 
129 	private final TestIdMap fIds = new TestIdMap();
130 
131 	private String[] fFailureNames;
132 
133 	private ITestLoader fLoader;
134 
135 	private MessageSender fSender;
136 
137 	private boolean fConsoleMode = false;
138 
139 	/**
140 	 * Reader thread that processes messages from the client.
141 	 */
142 	private class ReaderThread extends Thread {
ReaderThread()143 		public ReaderThread() {
144 			super("ReaderThread"); //$NON-NLS-1$
145 		}
146 
147 		@Override
run()148 		public void run(){
149 			try {
150 				String message= null;
151 				while (true) {
152 					if ((message= fReader.readLine()) != null) {
153 
154 						if (message.startsWith(MessageIds.TEST_STOP)){
155 							fStopped= true;
156 							RemoteTestRunner.this.stop();
157 							synchronized(RemoteTestRunner.this) {
158 								RemoteTestRunner.this.notifyAll();
159 							}
160 							break;
161 						}
162 
163 						else if (message.startsWith(MessageIds.TEST_RERUN)) {
164 							String arg= message.substring(MessageIds.MSG_HEADER_LENGTH);
165 							//format: testId className testName
166 							int c0= arg.indexOf(' ');
167 							int c1= arg.indexOf(' ', c0+1);
168 							String s= arg.substring(0, c0);
169 							int testId= Integer.parseInt(s);
170 							String className= arg.substring(c0+1, c1);
171 							String testName= arg.substring(c1+1, arg.length());
172 							synchronized(RemoteTestRunner.this) {
173 								fRerunRequests.add(new RerunRequest(testId, className, testName));
174 								RemoteTestRunner.this.notifyAll();
175 							}
176 						}
177 					}
178 				}
179 			} catch (Exception e) {
180 				RemoteTestRunner.this.stop();
181 			}
182 		}
183 	}
184 
RemoteTestRunner()185 	public RemoteTestRunner() {
186 		setMessageSender(this);
187 	}
188 
setMessageSender(MessageSender sender)189 	public void setMessageSender(MessageSender sender) {
190 		fSender = sender;
191 	}
192 
193 	/**
194 	 * The main entry point.
195 	 *
196 	 * @param args Parameters:
197 	 * <pre>-classnames: the name of the test suite class
198 	 * -testfilename: the name of a file containing classnames of test suites
199 	 * -test: the test method name (format classname testname)
200 	 * -packagenamefile: the name of a file containing package names of tests
201 	 * -host: the host to connect to default local host
202 	 * -port: the port to connect to, mandatory argument
203 	 * -keepalive: keep the process alive after a test run
204      * </pre>
205      */
main(String[] args)206 	public static void main(String[] args) {
207 		try {
208 			RemoteTestRunner testRunServer= new RemoteTestRunner();
209 			testRunServer.init(args);
210 			testRunServer.run();
211 		} catch (Throwable e) {
212 			e.printStackTrace(); // don't allow System.exit(0) to swallow exceptions
213 		} finally {
214 			// fix for 14434
215 			System.exit(0);
216 		}
217 	}
218 
219 	/**
220 	 * Parse command line arguments. Hook for subclasses to process
221 	 * additional arguments.
222 	 * @param args the arguments
223 	 */
init(String[] args)224 	protected void init(String[] args) {
225 		defaultInit(args);
226 	}
227 
228 	/**
229 	 * The class loader to be used for loading tests.
230 	 * Subclasses may override to use another class loader.
231 	 * @return the class loader to lead test classes
232 	 */
getTestClassLoader()233 	protected ClassLoader getTestClassLoader() {
234 		return getClass().getClassLoader();
235 	}
236 
237 	/**
238 	 * Process the default arguments.
239 	 * @param args arguments
240 	 */
defaultInit(String[] args)241 	protected final void defaultInit(String[] args) {
242 		for(int i= 0; i < args.length; i++) {
243 			if(args[i].toLowerCase().equals("-classnames") || args[i].toLowerCase().equals("-classname")){ //$NON-NLS-1$ //$NON-NLS-2$
244 				Vector<String> list= new Vector<String>();
245 				for (int j= i+1; j < args.length; j++) {
246 					if (args[j].startsWith("-")) //$NON-NLS-1$
247 						break;
248 					list.add(args[j]);
249 				}
250 				fTestClassNames= list.toArray(new String[list.size()]);
251 			}
252 			else if(args[i].toLowerCase().equals("-test")) { //$NON-NLS-1$
253 				String testName= args[i+1];
254 				int p= testName.indexOf(':');
255 				if (p == -1)
256 					throw new IllegalArgumentException("Testname not separated by \'%\'"); //$NON-NLS-1$
257 				fTestName= testName.substring(p+1);
258 				fTestClassNames= new String[]{ testName.substring(0, p)  };
259 				i++;
260 			}
261 			else if(args[i].toLowerCase().equals("-testnamefile")) { //$NON-NLS-1$
262 				String testNameFile= args[i+1];
263 				try {
264 					readTestNames(testNameFile);
265 				} catch (IOException e) {
266 					throw new IllegalArgumentException("Cannot read testname file.");		 //$NON-NLS-1$
267 				}
268 				i++;
269 
270 			} else if (args[i].toLowerCase().equals("-packagenamefile")) { //$NON-NLS-1$
271 				String pkgNameFile= args[i+1];
272 				try {
273 					readPackageNames(pkgNameFile);
274 				} catch (IOException e) {
275 					throw new IllegalArgumentException("Cannot read packagename file.");		 //$NON-NLS-1$
276 				}
277 				i++;
278 
279 			} else if (args[i].toLowerCase().equals("-testfailures")) { //$NON-NLS-1$
280 				String testFailuresFile= args[i+1];
281 				try {
282 					readFailureNames(testFailuresFile);
283 				} catch (IOException e) {
284 					throw new IllegalArgumentException("Cannot read testfailures file.");		 //$NON-NLS-1$
285 				}
286 				i++;
287 
288 			} else if(args[i].toLowerCase().equals("-port")) { //$NON-NLS-1$
289 				fPort= Integer.parseInt(args[i+1]);
290 				i++;
291 			}
292 			else if(args[i].toLowerCase().equals("-host")) { //$NON-NLS-1$
293 				fHost= args[i+1];
294 				i++;
295 			}
296 			else if(args[i].toLowerCase().equals("-rerun")) { //$NON-NLS-1$
297 				fRerunTest= args[i+1];
298 				i++;
299 			}
300 			else if(args[i].toLowerCase().equals("-keepalive")) { //$NON-NLS-1$
301 				fKeepAlive= true;
302 			}
303 			else if(args[i].toLowerCase().equals("-debugging") || args[i].toLowerCase().equals("-debug")){ //$NON-NLS-1$ //$NON-NLS-2$
304 			    fDebugMode= true;
305 
306 			} else if (args[i].toLowerCase().equals("-junitconsole")) { //$NON-NLS-1$
307 			    fConsoleMode  = true;
308 			} else if (args[i].toLowerCase().equals("-testloaderclass")) { //$NON-NLS-1$
309 				String className = args[i + 1];
310 				createLoader(className);
311 				i++;
312 			} else if(args[i].toLowerCase().equals("-uniqueid")) { //$NON-NLS-1$
313 				fUniqueId= args[i+1];
314 				i++;
315 			} else if (args[i].toLowerCase().equals("--include-tag")) { //$NON-NLS-1$
316 				String[] includeTags= fIncludeExcludeTags[0];
317 				if (includeTags == null) {
318 					includeTags= new String[1];
319 					includeTags[0]= args[i + 1];
320 				} else {
321 					String[] tags= new String[includeTags.length + 1];
322 					System.arraycopy(includeTags, 0, tags, 0, includeTags.length);
323 					tags[includeTags.length]= args[i + 1];
324 					includeTags= tags;
325 				}
326 				fIncludeExcludeTags[0]= includeTags;
327 				i++;
328 			} else if (args[i].toLowerCase().equals("--exclude-tag")) { //$NON-NLS-1$
329 				String[] excludeTags= fIncludeExcludeTags[1];
330 				if (excludeTags == null) {
331 					excludeTags= new String[1];
332 					excludeTags[0]= args[i + 1];
333 				} else {
334 					String[] tags= new String[excludeTags.length + 1];
335 					System.arraycopy(excludeTags, 0, tags, 0, excludeTags.length);
336 					tags[excludeTags.length]= args[i + 1];
337 					excludeTags= tags;
338 				}
339 				fIncludeExcludeTags[1]= excludeTags;
340 				i++;
341 			}
342 		}
343 
344 		if (getTestLoader() == null)
345 			initDefaultLoader();
346 
347 		if(fTestClassNames == null || fTestClassNames.length == 0) {
348 			if (fPackageNames == null || fPackageNames.length == 0) {
349 				throw new IllegalArgumentException(JUnitMessages.getString("RemoteTestRunner.error.classnamemissing")); //$NON-NLS-1$
350 			} else {
351 				fTestClassNames= new String[0];
352 			}
353 		}
354 
355 		if (fPort == -1)
356 			throw new IllegalArgumentException(JUnitMessages.getString("RemoteTestRunner.error.portmissing")); //$NON-NLS-1$
357 		if (fDebugMode)
358 			System.out.println("keepalive "+fKeepAlive); //$NON-NLS-1$
359 	}
360 
initDefaultLoader()361 	public void initDefaultLoader() {
362 		createLoader(JUnit3TestLoader.class.getName());
363 	}
364 
createLoader(String className)365 	public void createLoader(String className) {
366 		setLoader(createRawTestLoader(className));
367 	}
368 
createRawTestLoader(String className)369 	protected ITestLoader createRawTestLoader(String className) {
370 		try {
371 			return (ITestLoader) loadTestLoaderClass(className).newInstance();
372 		} catch (Exception e) {
373 			StringWriter trace= new StringWriter();
374 			e.printStackTrace(new PrintWriter(trace));
375 			String message= JUnitMessages.getFormattedString("RemoteTestRunner.error.invalidloader", new Object[] {className, trace.toString()}); //$NON-NLS-1$
376 			throw new IllegalArgumentException(message);
377 		}
378 	}
379 
loadTestLoaderClass(String className)380 	protected Class<?> loadTestLoaderClass(String className) throws ClassNotFoundException {
381 		return Class.forName(className);
382 	}
383 
setLoader(ITestLoader newInstance)384 	public void setLoader(ITestLoader newInstance) {
385 		fLoader = newInstance;
386 	}
387 
readPackageNames(String pkgNameFile)388 	private void readPackageNames(String pkgNameFile) throws IOException {
389 		BufferedReader br= new BufferedReader(new InputStreamReader(new FileInputStream(new File(pkgNameFile)), "UTF-8")); //$NON-NLS-1$
390 		try {
391 			String line;
392 			Vector<String> list= new Vector<String>();
393 			while ((line= br.readLine()) != null) {
394 				list.add(line);
395 			}
396 			fPackageNames= list.toArray(new String[list.size()]);
397 		}
398 		finally {
399 			br.close();
400 		}
401 		if (fDebugMode) {
402 			System.out.println("Packages:"); //$NON-NLS-1$
403 			for (int i= 0; i < fPackageNames.length; i++) {
404 				System.out.println("    "+fPackageNames[i]); //$NON-NLS-1$
405 			}
406 		}
407 	}
408 
readTestNames(String testNameFile)409 	private void readTestNames(String testNameFile) throws IOException {
410 		BufferedReader br= new BufferedReader(new InputStreamReader(new FileInputStream(new File(testNameFile)), "UTF-8")); //$NON-NLS-1$
411 		try {
412 			String line;
413 			Vector<String> list= new Vector<String>();
414 			while ((line= br.readLine()) != null) {
415 				list.add(line);
416 			}
417 			fTestClassNames= list.toArray(new String[list.size()]);
418 		}
419 		finally {
420 			br.close();
421 		}
422 		if (fDebugMode) {
423 			System.out.println("Tests:"); //$NON-NLS-1$
424 			for (int i= 0; i < fTestClassNames.length; i++) {
425 				System.out.println("    "+fTestClassNames[i]); //$NON-NLS-1$
426 			}
427 		}
428 	}
429 
readFailureNames(String testFailureFile)430 	private void readFailureNames(String testFailureFile) throws IOException {
431 		BufferedReader br= new BufferedReader(new InputStreamReader(new FileInputStream(new File(testFailureFile)), "UTF-8")); //$NON-NLS-1$
432 		try {
433 			String line;
434 			Vector<String> list= new Vector<String>();
435 			while ((line= br.readLine()) != null) {
436 				list.add(line);
437 			}
438 			fFailureNames= list.toArray(new String[list.size()]);
439 		}
440 		finally {
441 			br.close();
442 		}
443 		if (fDebugMode) {
444 			System.out.println("Failures:"); //$NON-NLS-1$
445 			for (int i= 0; i < fFailureNames.length; i++) {
446 				System.out.println("    "+fFailureNames[i]); //$NON-NLS-1$
447 			}
448 		}
449 	}
450 
451 	/**
452 	 * Connects to the remote ports and runs the tests.
453 	 */
run()454 	protected void run() {
455 		if (!connect())
456 			return;
457 		if (fRerunTest != null) {
458 			rerunTest(new RerunRequest(Integer.parseInt(fRerunTest), fTestClassNames[0], fTestName));
459 			return;
460 		}
461 
462 		FirstRunExecutionListener listener= firstRunExecutionListener();
463 		fExecution= new TestExecution(listener, getClassifier());
464 		runTests(fExecution);
465 		if (fKeepAlive)
466 			waitForReruns();
467 
468 		shutDown();
469 
470 	}
471 
firstRunExecutionListener()472 	public FirstRunExecutionListener firstRunExecutionListener() {
473 		return new FirstRunExecutionListener(fSender, fIds);
474 	}
475 
476 	/**
477 	 * Waits for rerun requests until an explicit stop request
478 	 */
waitForReruns()479 	private synchronized void waitForReruns() {
480 		while (!fStopped) {
481 			try {
482 				wait();
483 				if (!fStopped && fRerunRequests.size() > 0) {
484 					RerunRequest r= fRerunRequests.remove(0);
485 					rerunTest(r);
486 				}
487 			} catch (InterruptedException e) {
488 			}
489 		}
490 	}
491 
runFailed(String message, Exception exception)492 	public void runFailed(String message, Exception exception) {
493 		//TODO: remove System.err.println?
494 		System.err.println(message);
495 		if (exception != null)
496 			exception.printStackTrace(System.err);
497 	}
498 
loadClasses(String[] testClassNames)499 	protected Class<?>[] loadClasses(String[] testClassNames) {
500 		Vector<Class<?>> classes= new Vector<Class<?>>();
501 		for (int i = 0; i < testClassNames.length; i++) {
502 			String name = testClassNames[i];
503 			Class<?> clazz = loadClass(name, this);
504 			if (clazz != null) {
505 				classes.add(clazz);
506 			}
507 		}
508 		return classes.toArray(new Class[classes.size()]);
509 	}
510 
notifyListenersOfTestEnd(TestExecution execution, long testStartTime)511 	protected void notifyListenersOfTestEnd(TestExecution execution,
512 			long testStartTime) {
513 		if (execution == null || execution.shouldStop())
514 			notifyTestRunStopped(System.currentTimeMillis() - testStartTime);
515 		else
516 			notifyTestRunEnded(System.currentTimeMillis() - testStartTime);
517 	}
518 
519 	/**
520 	 * Runs a set of tests.
521 	 * @param testClassNames classes to be run
522 	 * @param testName individual method to be run
523 	 * @param execution executor
524 	 */
runTests(String[] testClassNames, String testName, TestExecution execution)525 	public void runTests(String[] testClassNames, String testName, TestExecution execution) {
526 		ITestReference[] suites= fLoader.loadTests(loadClasses(testClassNames), testName, fFailureNames, fPackageNames, fIncludeExcludeTags, fUniqueId, this);
527 
528 		// count all testMethods and inform ITestRunListeners
529 		int count= countTests(suites);
530 
531 		notifyTestRunStarted(count);
532 
533 		// test count is 0 if only dynamic tests will be run (i.e. only @TestFactory methods are present), hence test run should continue.
534 		/*if (count == 0) {
535 			notifyTestRunEnded(0);
536 			return;
537 		}*/
538 
539 		sendTrees(suites);
540 
541 		long testStartTime= System.currentTimeMillis();
542 		execution.run(suites);
543 		notifyListenersOfTestEnd(execution, testStartTime);
544 	}
545 
sendTrees(ITestReference[] suites)546 	private void sendTrees(ITestReference[] suites) {
547 		long startTime = System.currentTimeMillis();
548 		if (fDebugMode)
549 			System.out.print("start send tree..."); //$NON-NLS-1$
550 		for (int i = 0; i < suites.length; i++) {
551 			suites[i].sendTree(this);
552 			}
553 		if (fDebugMode)
554 			System.out.println("done send tree - time(ms): " + (System.currentTimeMillis() - startTime)); //$NON-NLS-1$
555 	}
556 
countTests(ITestReference[] tests)557 	private int countTests(ITestReference[] tests) {
558 		int count= 0;
559 		for (int i= 0; i < tests.length; i++) {
560 			ITestReference test= tests[i];
561 			if (test != null)
562 				count= count + test.countTestCases();
563 		}
564 		return count;
565 	}
566 
567 	/**
568 	 * Reruns a test as defined by the fully qualified class name and
569 	 * the name of the test.
570 	 * @param r rerun request
571 	 */
rerunTest(RerunRequest r)572 	public void rerunTest(RerunRequest r) {
573 		final Class<?>[] classes= loadClasses(new String[] { r.fRerunClassName });
574 		ITestReference rerunTest1= fLoader.loadTests(classes, r.fRerunTestName, null, null, fIncludeExcludeTags, fUniqueId, this)[0];
575 		RerunExecutionListener service= rerunExecutionListener();
576 
577 		TestExecution execution= new TestExecution(service, getClassifier());
578 		ITestReference[] suites= new ITestReference[] { rerunTest1 };
579 		execution.run(suites);
580 
581 		notifyRerunComplete(r, service.getStatus());
582 	}
583 
rerunExecutionListener()584 	public RerunExecutionListener rerunExecutionListener() {
585 		return new RerunExecutionListener(fSender, fIds);
586 	}
587 
getClassifier()588 	protected IClassifiesThrowables getClassifier() {
589 		return new DefaultClassifier();
590 	}
591 
visitTreeEntry(ITestIdentifier identifier, boolean hasChildren, int testCount, boolean isDynamicTest, String parentId)592 	public void visitTreeEntry(ITestIdentifier identifier, boolean hasChildren, int testCount, boolean isDynamicTest, String parentId) {
593 		String treeEntry= getTestId(identifier) + ',' + escapeText(identifier.getName()) + ',' + hasChildren + ',' + testCount
594 				+ ',' + isDynamicTest + ',' + parentId + ',' + escapeText(identifier.getDisplayName()) + ',' + escapeText(identifier.getParameterTypes())
595 				+ ',' + escapeText(identifier.getUniqueId());
596 		notifyTestTreeEntry(treeEntry);
597 	}
598 
escapeText(String s)599 	public static String escapeText(String s) {
600 		if ((s.indexOf(',') < 0) && (s.indexOf('\\') < 0) && (s.indexOf('\r') < 0) && (s.indexOf('\n') < 0))
601 			return s;
602 		StringBuffer sb= new StringBuffer(s.length()+10);
603 		for (int i= 0; i < s.length(); i++) {
604 			char c= s.charAt(i);
605 			switch (c) {
606 			case ',':
607 				sb.append("\\,"); //$NON-NLS-1$
608 				break;
609 			case '\\':
610 				sb.append("\\\\"); //$NON-NLS-1$
611 				break;
612 			case '\r':
613 				if (i + 1 < s.length() && s.charAt(i + 1) == '\n') {
614 					i++;
615 				}
616 				sb.append(' ');
617 				break;
618 			case '\n':
619 				sb.append(' ');
620 				break;
621 			default:
622 				sb.append(c);
623 				break;
624 			}
625 		}
626 		return sb.toString();
627 	}
628 
629 	// WANT: work in bug fixes since RC2?
getTestId(ITestIdentifier id)630 	public String getTestId(ITestIdentifier id) {
631 		return fIds.getTestId(id);
632 	}
633 
634 	/**
635 	 * Stop the current test run.
636 	 */
stop()637 	protected void stop() {
638 		if (fExecution != null) {
639 			fExecution.stop();
640 		}
641 	}
642 
643 	/**
644 	 * Connect to the remote test listener.
645 	 *
646 	 * @return <code>true</code> if connection successful, <code>false</code> if failed
647 	 */
connect()648 	protected boolean connect() {
649 		if (fConsoleMode) {
650 			fClientSocket = null;
651 			fWriter = new PrintWriter(System.out);
652 			fReader = new BufferedReader(new InputStreamReader(System.in));
653 			fReaderThread= new ReaderThread();
654 			fReaderThread.start();
655 			return true;
656 		}
657 		if (fDebugMode)
658 			System.out.println("RemoteTestRunner: trying to connect" + fHost + ":" + fPort); //$NON-NLS-1$ //$NON-NLS-2$
659 		Exception exception= null;
660 		for (int i= 1; i < 20; i++) {
661 			try{
662 				fClientSocket= new Socket(fHost, fPort);
663 				try {
664 				    fWriter= new PrintWriter(new BufferedWriter(new OutputStreamWriter(fClientSocket.getOutputStream(), "UTF-8")), false/*true*/); //$NON-NLS-1$
665 	            } catch (UnsupportedEncodingException e1) {
666 	                fWriter= new PrintWriter(new BufferedWriter(new OutputStreamWriter(fClientSocket.getOutputStream())), false/*true*/);
667 	            }
668 				try {
669 				    fReader= new BufferedReader(new InputStreamReader(fClientSocket.getInputStream(), "UTF-8")); //$NON-NLS-1$
670                 } catch (UnsupportedEncodingException e1) {
671                     fReader= new BufferedReader(new InputStreamReader(fClientSocket.getInputStream()));
672                 }
673 				fReaderThread= new ReaderThread();
674 				fReaderThread.start();
675 				return true;
676 			} catch(IOException e){
677 				exception= e;
678 			}
679 			try {
680 				Thread.sleep(2000);
681 			} catch(InterruptedException e) {
682 			}
683 		}
684 		runFailed(JUnitMessages.getFormattedString("RemoteTestRunner.error.connect", new String[]{fHost, Integer.toString(fPort)} ), exception); //$NON-NLS-1$
685 		return false;
686 	}
687 
688 	/**
689 	 * Shutsdown the connection to the remote test listener.
690 	 */
shutDown()691 	private void shutDown() {
692 		if (fWriter != null) {
693 			fWriter.close();
694 			fWriter= null;
695 		}
696 		try {
697 			if (fReaderThread != null)   {
698 				// interrupt reader thread so that we don't block on close
699 				// on a lock held by the BufferedReader
700 				// fix for bug: 38955
701 				fReaderThread.interrupt();
702 			}
703 			if (fReader != null) {
704 				fReader.close();
705 				fReader= null;
706 			}
707 		} catch(IOException e) {
708 			if (fDebugMode)
709 				e.printStackTrace();
710 		}
711 
712 		try {
713 			if (fClientSocket != null) {
714 				fClientSocket.close();
715 				fClientSocket= null;
716 			}
717 		} catch(IOException e) {
718 			if (fDebugMode)
719 				e.printStackTrace();
720 		}
721 	}
722 
723 	/*
724 	 * @see org.eclipse.jdt.internal.junit.runner.MessageSender#sendMessage(java.lang.String)
725 	 */
sendMessage(String msg)726 	public void sendMessage(String msg) {
727 		if(fWriter == null)
728 			return;
729 		fWriter.println(msg);
730 //		if (!fConsoleMode)
731 //			System.out.println(msg);
732 	}
733 
notifyTestRunStarted(int testCount)734 	protected void notifyTestRunStarted(int testCount) {
735 		fSender.sendMessage(MessageIds.TEST_RUN_START + testCount + " " + "v2"); //$NON-NLS-1$ //$NON-NLS-2$
736 	}
737 
notifyTestRunEnded(long elapsedTime)738 	private void notifyTestRunEnded(long elapsedTime) {
739 		fSender.sendMessage(MessageIds.TEST_RUN_END + elapsedTime);
740 		fSender.flush();
741 		//shutDown();
742 	}
743 
notifyTestRunStopped(long elapsedTime)744 	protected void notifyTestRunStopped(long elapsedTime) {
745 		fSender.sendMessage(MessageIds.TEST_STOPPED + elapsedTime);
746 		fSender.flush();
747 		//shutDown();
748 	}
749 
notifyTestTreeEntry(String treeEntry)750 	protected void notifyTestTreeEntry(String treeEntry) {
751 		fSender.sendMessage(MessageIds.TEST_TREE + treeEntry);
752 	}
753 
754 	/*
755 	 * @see org.eclipse.jdt.internal.junit.runner.RerunCompletionListener#notifyRerunComplete(org.eclipse.jdt.internal.junit.runner.RerunRequest,
756 	 *      java.lang.String)
757 	 */
notifyRerunComplete(RerunRequest r, String status)758 	public void notifyRerunComplete(RerunRequest r, String status) {
759 		if (fPort != -1) {
760 			fSender.sendMessage(MessageIds.TEST_RERAN + r.fRerunTestId + " " + r.fRerunClassName + " " + r.fRerunTestName + " " + status); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
761 			fSender.flush();
762 		}
763 	}
764 
flush()765 	public void flush() {
766 	    fWriter.flush();
767 	}
768 
runTests(TestExecution execution)769 	public void runTests(TestExecution execution) {
770 		runTests(fTestClassNames, fTestName, execution);
771 	}
772 
getTestLoader()773 	public ITestLoader getTestLoader() {
774 		return fLoader;
775 	}
776 
loadClass(String className, RemoteTestRunner listener)777 	public Class<?> loadClass(String className, RemoteTestRunner listener) {
778 		Class<?> clazz= null;
779 		try {
780 			clazz= getTestClassLoader().loadClass(className);
781 		} catch (ClassNotFoundException e) {
782 			listener.runFailed(JUnitMessages.getFormattedString("RemoteTestRunner.error.classnotfound", className), e); //$NON-NLS-1$
783 		}
784 		return clazz;
785 	}
786 }
787