Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

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 }