Source code: org/eclipse/jdt/internal/junit/runner/RemoteTestRunner.java
1 /*******************************************************************************
2 * Copyright (c) 2000, 2004 IBM Corporation and others.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Common Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/cpl-v10.html
7 *
8 * Contributors:
9 * IBM Corporation - initial API and implementation
10 * Sebastian Davids: sdavids@gmx.de bug 26754
11 *******************************************************************************/
12 package org.eclipse.jdt.internal.junit.runner;
13
14 import java.io.BufferedReader;
15 import java.io.BufferedWriter;
16 import java.io.File;
17 import java.io.FileReader;
18 import java.io.IOException;
19 import java.io.InputStreamReader;
20 import java.io.OutputStreamWriter;
21 import java.io.PrintWriter;
22 import java.io.StringWriter;
23 import java.io.UnsupportedEncodingException;
24 import java.lang.reflect.Constructor;
25 import java.lang.reflect.Field;
26 import java.lang.reflect.InvocationTargetException;
27 import java.lang.reflect.Method;
28 import java.lang.reflect.Modifier;
29 import java.net.Socket;
30 import java.util.Vector;
31 import junit.extensions.TestDecorator;
32 import junit.framework.AssertionFailedError;
33 import junit.framework.Test;
34 import junit.framework.TestCase;
35 import junit.framework.TestFailure;
36 import junit.framework.TestListener;
37 import junit.framework.TestResult;
38 import junit.framework.TestSuite;
39
40 /**
41 * A TestRunner that reports results via a socket connection.
42 * See MessageIds for more information about the protocl.
43 */
44 public class RemoteTestRunner implements TestListener {
45 /**
46 * Holder for information for a rerun request
47 */
48 private static class RerunRequest {
49 String fRerunClassName;
50 String fRerunTestName;
51 int fRerunTestId;
52
53 public RerunRequest(int testId, String className, String testName) {
54 fRerunTestId= testId;
55 fRerunClassName= className;
56 fRerunTestName= testName;
57 }
58
59 }
60
61 private static final String SET_UP_TEST_METHOD_NAME= "setUpTest"; //$NON-NLS-1$
62
63 private static final String SUITE_METHODNAME= "suite"; //$NON-NLS-1$
64
65 /**
66 * The name of the test classes to be executed
67 */
68 private String[] fTestClassNames;
69 /**
70 * The name of the test (argument -test)
71 */
72 private String fTestName;
73 /**
74 * The current test result
75 */
76 private TestResult fTestResult;
77
78 /**
79 * The version expected by the client
80 */
81 private String fVersion= ""; //$NON-NLS-1$
82
83 /**
84 * The client socket.
85 */
86 private Socket fClientSocket;
87 /**
88 * Print writer for sending messages
89 */
90 private PrintWriter fWriter;
91 /**
92 * Reader for incoming messages
93 */
94 private BufferedReader fReader;
95 /**
96 * Host to connect to, default is the localhost
97 */
98 private String fHost= ""; //$NON-NLS-1$
99 /**
100 * Port to connect to.
101 */
102 private int fPort= -1;
103 /**
104 * Is the debug mode enabled?
105 */
106 private boolean fDebugMode= false;
107 /**
108 * Keep the test run server alive after a test run has finished.
109 * This allows to rerun tests.
110 */
111 private boolean fKeepAlive= false;
112 /**
113 * Has the server been stopped
114 */
115 private boolean fStopped= false;
116 /**
117 * Queue of rerun requests.
118 */
119 private Vector fRerunRequests= new Vector(10);
120 /**
121 * Thread reading from the socket
122 */
123 private ReaderThread fReaderThread;
124
125 private String fRerunTest;
126
127 /**
128 * Map to map tests to unique IDs
129 */
130 private CustomHashtable fIdMap;
131 private int fNextId= 1;
132
133 /**
134 * Reader thread that processes messages from the client.
135 */
136 private class ReaderThread extends Thread {
137 public ReaderThread() {
138 super("ReaderThread"); //$NON-NLS-1$
139 }
140
141 public void run(){
142 try {
143 String message= null;
144 while (true) {
145 if ((message= fReader.readLine()) != null) {
146
147 if (message.startsWith(MessageIds.TEST_STOP)){
148 fStopped= true;
149 RemoteTestRunner.this.stop();
150 synchronized(RemoteTestRunner.this) {
151 RemoteTestRunner.this.notifyAll();
152 }
153 break;
154 }
155
156 else if (message.startsWith(MessageIds.TEST_RERUN)) {
157 String arg= message.substring(MessageIds.MSG_HEADER_LENGTH);
158 //format: testId className testName
159 int c0= arg.indexOf(' '); //$NON-NLS-1$
160 int c1= arg.indexOf(' ', c0+1);
161 String s= arg.substring(0, c0);
162 int testId= Integer.parseInt(s);
163 String className= arg.substring(c0+1, c1);
164 String testName= arg.substring(c1+1, arg.length());
165 synchronized(RemoteTestRunner.this) {
166 fRerunRequests.add(new RerunRequest(testId, className, testName));
167 RemoteTestRunner.this.notifyAll();
168 }
169 }
170 }
171 }
172 } catch (Exception e) {
173 RemoteTestRunner.this.stop();
174 }
175 }
176 }
177
178 /**
179 * The main entry point.
180 * Parameters<pre>
181 * -classnames: the name of the test suite class
182 * -testfilename: the name of a file containing classnames of test suites
183 * -test: the test method name (format classname testname)
184 * -host: the host to connect to default local host
185 * -port: the port to connect to, mandatory argument
186 * -keepalive: keep the process alive after a test run
187 * </pre>
188 */
189 public static void main(String[] args) {
190 RemoteTestRunner testRunServer= new RemoteTestRunner();
191 testRunServer.init(args);
192 testRunServer.run();
193 // fix for 14434
194 System.exit(0);
195 }
196
197 /**
198 * Parse command line arguments. Hook for subclasses to process
199 * additional arguments.
200 */
201 protected void init(String[] args) {
202 defaultInit(args);
203 }
204
205 /**
206 * The class loader to be used for loading tests.
207 * Subclasses may override to use another class loader.
208 */
209 protected ClassLoader getClassLoader() {
210 return getClass().getClassLoader();
211 }
212
213 /**
214 * Process the default arguments.
215 */
216 protected final void defaultInit(String[] args) {
217 for(int i= 0; i < args.length; i++) {
218 if(args[i].toLowerCase().equals("-classnames") || args[i].toLowerCase().equals("-classname")){ //$NON-NLS-1$ //$NON-NLS-2$
219 Vector list= new Vector();
220 for (int j= i+1; j < args.length; j++) {
221 if (args[j].startsWith("-")) //$NON-NLS-1$
222 break;
223 list.add(args[j]);
224 }
225 fTestClassNames= (String[]) list.toArray(new String[list.size()]);
226 }
227 else if(args[i].toLowerCase().equals("-test")) { //$NON-NLS-1$
228 String testName= args[i+1];
229 int p= testName.indexOf(':');
230 if (p == -1)
231 throw new IllegalArgumentException("Testname not separated by \'%\'"); //$NON-NLS-1$
232 fTestName= testName.substring(p+1);
233 fTestClassNames= new String[]{ testName.substring(0, p) };
234 i++;
235 }
236 else if(args[i].toLowerCase().equals("-testnamefile")) { //$NON-NLS-1$
237 String testNameFile= args[i+1];
238 try {
239 readTestNames(testNameFile);
240 } catch (IOException e) {
241 throw new IllegalArgumentException("Cannot read testname file."); //$NON-NLS-1$
242 }
243 i++;
244
245 } else if(args[i].toLowerCase().equals("-port")) { //$NON-NLS-1$
246 fPort= Integer.parseInt(args[i+1]);
247 i++;
248 }
249 else if(args[i].toLowerCase().equals("-host")) { //$NON-NLS-1$
250 fHost= args[i+1];
251 i++;
252 }
253 else if(args[i].toLowerCase().equals("-rerun")) { //$NON-NLS-1$
254 fRerunTest= args[i+1];
255 i++;
256 }
257 else if(args[i].toLowerCase().equals("-keepalive")) { //$NON-NLS-1$
258 fKeepAlive= true;
259 }
260 else if(args[i].toLowerCase().equals("-debugging") || args[i].toLowerCase().equals("-debug")){ //$NON-NLS-1$ //$NON-NLS-2$
261 fDebugMode= true;
262 }
263 else if(args[i].toLowerCase().equals("-version")){ //$NON-NLS-1$
264 fVersion= args[i+1];
265 i++;
266 }
267 }
268 if(fTestClassNames == null || fTestClassNames.length == 0)
269 throw new IllegalArgumentException(JUnitMessages.getString("RemoteTestRunner.error.classnamemissing")); //$NON-NLS-1$
270
271 if (fPort == -1)
272 throw new IllegalArgumentException(JUnitMessages.getString("RemoteTestRunner.error.portmissing")); //$NON-NLS-1$
273 if (fDebugMode)
274 System.out.println("keepalive "+fKeepAlive); //$NON-NLS-1$
275 }
276
277 private void readTestNames(String testNameFile) throws IOException {
278 BufferedReader br= new BufferedReader(new FileReader(new File(testNameFile)));
279 try {
280 String line;
281 Vector list= new Vector();
282 while ((line= br.readLine()) != null) {
283 list.add(line);
284 }
285 fTestClassNames= (String[]) list.toArray(new String[list.size()]);
286 }
287 finally {
288 br.close();
289 }
290 if (fDebugMode) {
291 System.out.println("Tests:"); //$NON-NLS-1$
292 for (int i= 0; i < fTestClassNames.length; i++) {
293 System.out.println(" "+fTestClassNames[i]); //$NON-NLS-1$
294 }
295 }
296 }
297
298
299 /**
300 * Connects to the remote ports and runs the tests.
301 */
302 protected void run() {
303 if (!connect())
304 return;
305 if (fRerunTest != null) {
306 rerunTest(Integer.parseInt(fRerunTest), fTestClassNames[0], fTestName);
307 return;
308 }
309 fTestResult= new TestResult();
310 fTestResult.addListener(this);
311 runTests(fTestClassNames, fTestName);
312 fTestResult.removeListener(this);
313
314 if (fTestResult != null) {
315 fTestResult.stop();
316 fTestResult= null;
317 }
318 if (fKeepAlive)
319 waitForReruns();
320
321 shutDown();
322
323 }
324
325 /**
326 * Waits for rerun requests until an explicit stop request
327 */
328 private synchronized void waitForReruns() {
329 while (!fStopped) {
330 try {
331 wait();
332 if (!fStopped && fRerunRequests.size() > 0) {
333 RerunRequest r= (RerunRequest)fRerunRequests.remove(0);
334 rerunTest(r.fRerunTestId, r.fRerunClassName, r.fRerunTestName);
335 }
336 } catch (InterruptedException e) {
337 }
338 }
339 }
340
341 /**
342 * Returns the Test corresponding to the given suite.
343 */
344 private Test getTest(String suiteClassName, String testName) {
345 Class testClass= null;
346 try {
347 testClass= loadSuiteClass(suiteClassName);
348 } catch (ClassNotFoundException e) {
349 String clazz= e.getMessage();
350 if (clazz == null)
351 clazz= suiteClassName;
352 runFailed(JUnitMessages.getFormattedString("RemoteTestRunner.error.classnotfound", clazz)); //$NON-NLS-1$
353 return null;
354 } catch(Exception e) {
355 runFailed(JUnitMessages.getFormattedString("RemoteTestRunner.error.exception", e )); //$NON-NLS-1$
356 return null;
357 }
358 if (testName != null) {
359 return setupTest(testClass, createTest(testName, testClass));
360 }
361 Method suiteMethod= null;
362 try {
363 suiteMethod= testClass.getMethod(SUITE_METHODNAME, new Class[0]);
364 } catch(Exception e) {
365 // try to extract a test suite automatically
366 return new TestSuite(testClass);
367 }
368 Test test= null;
369 try {
370 test= (Test)suiteMethod.invoke(null, new Class[0]); // static method
371 }
372 catch (InvocationTargetException e) {
373 runFailed(JUnitMessages.getFormattedString("RemoteTestRunner.error.invoke", e.getTargetException().toString() )); //$NON-NLS-1$
374 return null;
375 }
376 catch (IllegalAccessException e) {
377 runFailed(JUnitMessages.getFormattedString("RemoteTestRunner.error.invoke", e.toString() )); //$NON-NLS-1$
378 return null;
379 }
380 return test;
381 }
382
383 protected void runFailed(String message) {
384 System.err.println(message);
385 }
386
387 /**
388 * Loads the test suite class.
389 */
390 private Class loadSuiteClass(String className) throws ClassNotFoundException {
391 if (className == null)
392 return null;
393 return getClassLoader().loadClass(className);
394 }
395
396 /**
397 * Runs a set of tests.
398 */
399 private void runTests(String[] testClassNames, String testName) {
400 // instantiate all tests
401 Test[] suites= new Test[testClassNames.length];
402
403 for (int i= 0; i < suites.length; i++) {
404 suites[i]= getTest(testClassNames[i], testName);
405 }
406
407 // count all testMethods and inform ITestRunListeners
408 int count= countTests(suites);
409 fIdMap= new CustomHashtable(count, new IElementComparer() {
410 public boolean equals(Object a, Object b) {
411 return a == b;
412 }
413 public int hashCode(Object element) {
414 return System.identityHashCode(element);
415 }
416 });
417
418 notifyTestRunStarted(count);
419
420 if (count == 0) {
421 notifyTestRunEnded(0);
422 return;
423 }
424
425 long startTime= System.currentTimeMillis();
426 if (fDebugMode)
427 System.out.print("start send tree..."); //$NON-NLS-1$
428 for (int i= 0; i < suites.length; i++) {
429 sendTree(suites[i]);
430 }
431 if (fDebugMode)
432 System.out.println("done send tree - time(ms): "+(System.currentTimeMillis()-startTime)); //$NON-NLS-1$
433
434 long testStartTime= System.currentTimeMillis();
435 for (int i= 0; i < suites.length; i++) {
436 suites[i].run(fTestResult);
437 }
438 // inform ITestRunListeners of test end
439 if (fTestResult == null || fTestResult.shouldStop())
440 notifyTestRunStopped(System.currentTimeMillis() - testStartTime);
441 else
442 notifyTestRunEnded(System.currentTimeMillis() - testStartTime);
443 }
444
445 private int countTests(Test[] tests) {
446 int count= 0;
447 for (int i= 0; i < tests.length; i++) {
448 if (tests[i] != null)
449 count= count + tests[i].countTestCases();
450 }
451 return count;
452 }
453
454 /**
455 * Reruns a test as defined by the fully qualified class name and
456 * the name of the test.
457 */
458 public void rerunTest(int testId, String className, String testName) {
459 Test reloadedTest= null;
460 Class reloadedTestClass= null;
461 try {
462 reloadedTestClass= getClassLoader().loadClass(className);
463 reloadedTest= createTest(testName, reloadedTestClass);
464 } catch(Exception e) {
465 reloadedTest= warning(JUnitMessages.getFormattedString("RemoteTestRunner.error.couldnotcreate", testName)); //$NON-NLS-1$
466 }
467 Test rerunTest= setupTest(reloadedTestClass, reloadedTest);
468 TestResult result= new TestResult();
469 rerunTest.run(result);
470 notifyTestReran(result, Integer.toString(testId), className, testName);
471 }
472
473 /**
474 * Prepare a single test to be run standalone. If the test case class provides
475 * a static method Test setUpTest(Test test) then this method will be invoked.
476 * Instead of calling the test method directly the "decorated" test returned from
477 * setUpTest will be called. The purpose of this mechanism is to enable
478 * tests which requires a set-up to be run individually.
479 */
480 private Test setupTest(Class reloadedTestClass, Test reloadedTest) {
481 Method setup= null;
482 try {
483 setup= reloadedTestClass.getMethod(SET_UP_TEST_METHOD_NAME, new Class[] {Test.class});
484 } catch (SecurityException e1) {
485 return reloadedTest;
486 } catch (NoSuchMethodException e) {
487 return reloadedTest;
488 }
489 if (setup.getReturnType() != Test.class)
490 return warning(JUnitMessages.getString("RemoteTestRunner.error.notestreturn")); //$NON-NLS-1$
491 if (!Modifier.isPublic(setup.getModifiers()))
492 return warning(JUnitMessages.getString("RemoteTestRunner.error.shouldbepublic")); //$NON-NLS-1$
493 if (!Modifier.isStatic(setup.getModifiers()))
494 return warning(JUnitMessages.getString("RemoteTestRunner.error.shouldbestatic")); //$NON-NLS-1$
495 try {
496 Test test= (Test)setup.invoke(null, new Object[] {reloadedTest});
497 if (test == null)
498 return warning(JUnitMessages.getString("RemoteTestRunner.error.nullreturn")); //$NON-NLS-1$
499 return test;
500 } catch (IllegalArgumentException e) {
501 return warning(JUnitMessages.getFormattedString("RemoteTestRunner.error.couldnotinvoke", e)); //$NON-NLS-1$
502 } catch (IllegalAccessException e) {
503 return warning(JUnitMessages.getFormattedString("RemoteTestRunner.error.couldnotinvoke", e)); //$NON-NLS-1$
504 } catch (InvocationTargetException e) {
505 return warning(JUnitMessages.getFormattedString("RemoteTestRunner.error.invocationexception", e.getTargetException())); //$NON-NLS-1$
506 }
507 }
508
509 /**
510 * Returns a test which will fail and log a warning message.
511 */
512 private Test warning(final String message) {
513 return new TestCase("warning") { //$NON-NLS-1$
514 protected void runTest() {
515 fail(message);
516 }
517 };
518 }
519
520 private Test createTest(String testName, Class testClass) {
521 Class[] classArgs= { String.class };
522 Test test;
523 Constructor constructor= null;
524 try {
525 try {
526 constructor= testClass.getConstructor(classArgs);
527 test= (Test)constructor.newInstance(new Object[]{testName});
528 } catch (NoSuchMethodException e) {
529 // try the no arg constructor supported in 3.8.1
530 constructor= testClass.getConstructor(new Class[0]);
531 test= (Test)constructor.newInstance(new Object[0]);
532 if (test instanceof TestCase)
533 ((TestCase) test).setName(testName);
534 }
535 if (test != null)
536 return test;
537 } catch (InstantiationException e) {
538 } catch (IllegalAccessException e) {
539 } catch (InvocationTargetException e) {
540 } catch (NoSuchMethodException e) {
541 } catch (ClassCastException e) {
542 }
543 return warning("Could not create test \'"+testName+"\' "); //$NON-NLS-1$ //$NON-NLS-2$
544 }
545
546
547 /*
548 * @see TestListener#addError(Test, Throwable)
549 */
550 public final void addError(Test test, Throwable throwable) {
551 notifyTestFailed(test, MessageIds.TEST_ERROR, getTrace(throwable));
552 }
553
554 /*
555 * @see TestListener#addFailure(Test, AssertionFailedError)
556 */
557 public final void addFailure(Test test, AssertionFailedError assertionFailedError) {
558 if ("3".equals(fVersion)) { //$NON-NLS-1$
559 if (isComparisonFailure(assertionFailedError)) {
560 // transmit the expected and the actual string
561 String expected = getField(assertionFailedError, "fExpected"); //$NON-NLS-1$
562 String actual = getField(assertionFailedError, "fActual"); //$NON-NLS-1$
563 if (expected != null && actual != null) {
564 notifyTestFailed2(test, MessageIds.TEST_FAILED, getTrace(assertionFailedError), expected, actual);
565 return;
566 }
567 }
568 }
569 notifyTestFailed(test, MessageIds.TEST_FAILED, getTrace(assertionFailedError));
570 }
571
572 private boolean isComparisonFailure(Throwable throwable) {
573 // avoid reference to comparison failure to avoid a dependency on 3.8.1
574 return throwable.getClass().getName().equals("junit.framework.ComparisonFailure"); //$NON-NLS-1$
575 }
576
577 /*
578 * @see TestListener#endTest(Test)
579 */
580 public void endTest(Test test) {
581 notifyTestEnded(test);
582 }
583
584 /*
585 * @see TestListener#startTest(Test)
586 */
587 public void startTest(Test test) {
588 notifyTestStarted(test);
589 }
590
591 private void sendTree(Test test){
592 if(test instanceof TestDecorator){
593 TestDecorator decorator= (TestDecorator) test;
594 sendTree(decorator.getTest());
595 }
596 else if(test instanceof TestSuite){
597 TestSuite suite= (TestSuite) test;
598 notifyTestTreeEntry(getTestId(test)+','+escapeComma(suite.toString().trim()) + ',' + true + ',' + suite.testCount());
599 for(int i=0; i < suite.testCount(); i++){
600 sendTree(suite.testAt(i));
601 }
602 }
603 else {
604 notifyTestTreeEntry(getTestId(test)+ ',' + escapeComma(getTestName(test).trim()) + ',' + false + ',' + test.countTestCases());
605 }
606 }
607
608 private String escapeComma(String s) {
609 if ((s.indexOf(',') < 0) && (s.indexOf('\\') < 0))
610 return s;
611 StringBuffer sb= new StringBuffer(s.length()+10);
612 for (int i= 0; i < s.length(); i++) {
613 char c= s.charAt(i);
614 if (c == ',')
615 sb.append("\\,"); //$NON-NLS-1$
616 else if (c == '\\')
617 sb.append("\\\\"); //$NON-NLS-1$
618 else
619 sb.append(c);
620 }
621 return sb.toString();
622 }
623
624 private String getTestId(Test test) {
625 Object id= fIdMap.get(test);
626 if (id != null)
627 return (String)id;
628 String newId= Integer.toString(fNextId++);
629 fIdMap.put(test, newId);
630 return newId;
631 }
632
633 private String getTestName(Test test) {
634 if (test instanceof TestCase) {
635 TestCase testCase= (TestCase) test;
636 return JUnitMessages.getFormattedString("RemoteTestRunner.testName", new String[] {testCase.getName(), test.getClass().getName()}); //$NON-NLS-1$
637 }
638 if (test instanceof TestSuite) {
639 TestSuite suite= (TestSuite) test;
640 if (suite.getName() != null)
641 return suite.getName();
642 return getClass().getName();
643 }
644 return test.toString();
645 }
646
647 /**
648 * Returns the stack trace for the given throwable.
649 */
650 private String getTrace(Throwable t) {
651 StringWriter stringWriter= new StringWriter();
652 PrintWriter writer= new PrintWriter(stringWriter);
653 t.printStackTrace(writer);
654 StringBuffer buffer= stringWriter.getBuffer();
655 return buffer.toString();
656 }
657
658 /**
659 * Stop the current test run.
660 */
661 protected void stop() {
662 if (fTestResult != null) {
663 fTestResult.stop();
664 }
665 }
666
667 /**
668 * Connect to the remote test listener.
669 */
670 private boolean connect() {
671 if (fDebugMode)
672 System.out.println("RemoteTestRunner: trying to connect" + fHost + ":" + fPort); //$NON-NLS-1$ //$NON-NLS-2$
673 Exception exception= null;
674 for (int i= 1; i < 20; i++) {
675 try{
676 fClientSocket= new Socket(fHost, fPort);
677 try {
678 fWriter= new PrintWriter(new BufferedWriter(new OutputStreamWriter(fClientSocket.getOutputStream(), "UTF-8")), false/*true*/); //$NON-NLS-1$
679 } catch (UnsupportedEncodingException e1) {
680 fWriter= new PrintWriter(new BufferedWriter(new OutputStreamWriter(fClientSocket.getOutputStream())), false/*true*/);
681 }
682 try {
683 fReader= new BufferedReader(new InputStreamReader(fClientSocket.getInputStream(), "UTF-8")); //$NON-NLS-1$
684 } catch (UnsupportedEncodingException e1) {
685 fReader= new BufferedReader(new InputStreamReader(fClientSocket.getInputStream()));
686 }
687 fReaderThread= new ReaderThread();
688 fReaderThread.start();
689 return true;
690 } catch(IOException e){
691 exception= e;
692 }
693 try {
694 Thread.sleep(2000);
695 } catch(InterruptedException e) {
696 }
697 }
698 runFailed(JUnitMessages.getFormattedString("RemoteTestRunner.error.connect", new String[]{fHost, Integer.toString(fPort)} )); //$NON-NLS-1$
699 exception.printStackTrace();
700 return false;
701 }
702
703 /**
704 * Shutsdown the connection to the remote test listener.
705 */
706 private void shutDown() {
707 if (fWriter != null) {
708 fWriter.close();
709 fWriter= null;
710 }
711 try {
712 if (fReaderThread != null) {
713 // interrupt reader thread so that we don't block on close
714 // on a lock held by the BufferedReader
715 // fix for bug: 38955
716 fReaderThread.interrupt();
717 }
718 if (fReader != null) {
719 fReader.close();
720 fReader= null;
721 }
722 } catch(IOException e) {
723 if (fDebugMode)
724 e.printStackTrace();
725 }
726
727 try {
728 if (fClientSocket != null) {
729 fClientSocket.close();
730 fClientSocket= null;
731 }
732 } catch(IOException e) {
733 if (fDebugMode)
734 e.printStackTrace();
735 }
736 }
737
738
739 private void sendMessage(String msg) {
740 if(fWriter == null)
741 return;
742 fWriter.println(msg);
743 }
744
745
746 private void notifyTestRunStarted(int testCount) {
747 sendMessage(MessageIds.TEST_RUN_START + testCount + " " + "v2"); //$NON-NLS-1$ //$NON-NLS-2$
748 }
749
750
751 private void notifyTestRunEnded(long elapsedTime) {
752 sendMessage(MessageIds.TEST_RUN_END + elapsedTime);
753 fWriter.flush();
754 //shutDown();
755 }
756
757
758 private void notifyTestRunStopped(long elapsedTime) {
759 sendMessage(MessageIds.TEST_STOPPED + elapsedTime );
760 fWriter.flush();
761 //shutDown();
762 }
763
764 private void notifyTestStarted(Test test) {
765 sendMessage(MessageIds.TEST_START + getTestId(test) + ','+test.toString());
766 fWriter.flush();
767 }
768
769 private void notifyTestEnded(Test test) {
770 sendMessage(MessageIds.TEST_END + getTestId(test)+','+getTestName(test));
771 }
772
773 private void notifyTestFailed(Test test, String status, String trace) {
774 sendMessage(status + getTestId(test) + ',' + getTestName(test));
775 sendMessage(MessageIds.TRACE_START);
776 sendMessage(trace);
777 sendMessage(MessageIds.TRACE_END);
778 fWriter.flush();
779 }
780
781 private void notifyTestFailed2(Test test, String status, String trace, String expected, String actual) {
782 sendMessage(status + getTestId(test) + ',' + getTestName(test));
783
784 sendMessage(MessageIds.EXPECTED_START);
785 sendMessage(expected);
786 sendMessage(MessageIds.EXPECTED_END);
787
788 sendMessage(MessageIds.ACTUAL_START);
789 sendMessage(actual);
790 sendMessage(MessageIds.ACTUAL_END);
791
792 sendMessage(MessageIds.TRACE_START);
793 sendMessage(trace);
794 sendMessage(MessageIds.TRACE_END);
795
796 fWriter.flush();
797 }
798
799 private void notifyTestTreeEntry(String treeEntry) {
800 sendMessage(MessageIds.TEST_TREE + treeEntry);
801 }
802
803 private void notifyTestReran(TestResult result, String testId, String testClass, String testName) {
804 TestFailure failure= null;
805 if (result.errorCount() > 0) {
806 failure= (TestFailure)result.errors().nextElement();
807 }
808 if (result.failureCount() > 0) {
809 failure= (TestFailure)result.failures().nextElement();
810 }
811 if (failure != null) {
812 Throwable t= failure.thrownException();
813
814 if ("3".equals(fVersion)) { //$NON-NLS-1$
815 if (isComparisonFailure(t)) {
816 // transmit the expected and the actual string
817 String expected = getField(t, "fExpected"); //$NON-NLS-1$
818 String actual = getField(t, "fActual"); //$NON-NLS-1$
819 if (expected != null && actual != null) {
820 sendMessage(MessageIds.EXPECTED_START);
821 sendMessage(expected);
822 sendMessage(MessageIds.EXPECTED_END);
823
824 sendMessage(MessageIds.ACTUAL_START);
825 sendMessage(actual);
826 sendMessage(MessageIds.ACTUAL_END);
827 }
828 }
829 }
830 String trace= getTrace(t);
831 sendMessage(MessageIds.RTRACE_START);
832 sendMessage(trace);
833 sendMessage(MessageIds.RTRACE_END);
834 fWriter.flush();
835 }
836 String status= "OK"; //$NON-NLS-1$
837 if (result.errorCount() > 0)
838 status= "ERROR"; //$NON-NLS-1$
839 else if (result.failureCount() > 0)
840 status= "FAILURE"; //$NON-NLS-1$
841 if (fPort != -1) {
842 sendMessage(MessageIds.TEST_RERAN + testId+ " "+testClass+" "+testName+" "+status); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
843 fWriter.flush();
844 }
845 }
846
847 private String getField(Object object, String fieldName) {
848 Class clazz= object.getClass();
849 try {
850 Field field= clazz.getDeclaredField(fieldName);
851 field.setAccessible(true);
852 Object result= field.get(object);
853 return result.toString();
854 } catch (Exception e) {
855 // fall through
856 }
857 return null;
858 }
859 }