Home | History | Annotate | Download | only in remote
      1 package org.testng.remote;
      2 
      3 import static org.testng.internal.Utils.defaultIfStringEmpty;
      4 
      5 import com.beust.jcommander.JCommander;
      6 import com.beust.jcommander.ParameterException;
      7 
      8 import org.testng.CommandLineArgs;
      9 import org.testng.IClassListener;
     10 import org.testng.IInvokedMethodListener;
     11 import org.testng.ISuite;
     12 import org.testng.ISuiteListener;
     13 import org.testng.ITestRunnerFactory;
     14 import org.testng.TestNG;
     15 import org.testng.TestNGException;
     16 import org.testng.TestRunner;
     17 import org.testng.collections.Lists;
     18 import org.testng.remote.strprotocol.GenericMessage;
     19 import org.testng.remote.strprotocol.IMessageSender;
     20 import org.testng.remote.strprotocol.MessageHelper;
     21 import org.testng.remote.strprotocol.MessageHub;
     22 import org.testng.remote.strprotocol.RemoteTestListener;
     23 import org.testng.remote.strprotocol.SerializedMessageSender;
     24 import org.testng.remote.strprotocol.StringMessageSender;
     25 import org.testng.remote.strprotocol.SuiteMessage;
     26 import org.testng.reporters.JUnitXMLReporter;
     27 import org.testng.reporters.TestHTMLReporter;
     28 import org.testng.xml.XmlSuite;
     29 import org.testng.xml.XmlTest;
     30 
     31 import java.util.Arrays;
     32 import java.util.Collection;
     33 import java.util.List;
     34 
     35 /**
     36  * Extension of TestNG registering a remote TestListener.
     37  *
     38  * @author Cedric Beust <cedric (at) beust.com>
     39  */
     40 public class RemoteTestNG extends TestNG {
     41   private static final String LOCALHOST = "localhost";
     42 
     43   // The following constants are referenced by the Eclipse plug-in, make sure you
     44   // modify the plug-in as well if you change any of them.
     45   public static final String DEBUG_PORT = "12345";
     46   public static final String DEBUG_SUITE_FILE = "testng-customsuite.xml";
     47   public static final String DEBUG_SUITE_DIRECTORY = System.getProperty("java.io.tmpdir");
     48   public static final String PROPERTY_DEBUG = "testng.eclipse.debug";
     49   public static final String PROPERTY_VERBOSE = "testng.eclipse.verbose";
     50   // End of Eclipse constants.
     51 
     52   private ITestRunnerFactory m_customTestRunnerFactory;
     53   private String m_host;
     54 
     55   /** Port used for the string protocol */
     56   private Integer m_port = null;
     57 
     58   /** Port used for the serialized protocol */
     59   private static Integer m_serPort = null;
     60 
     61   private static boolean m_debug;
     62 
     63   private static boolean m_dontExit;
     64 
     65   private static boolean m_ack;
     66 
     67   public void setHost(String host) {
     68     m_host = defaultIfStringEmpty(host, LOCALHOST);
     69   }
     70 
     71   private void calculateAllSuites(List<XmlSuite> suites, List<XmlSuite> outSuites) {
     72     for (XmlSuite s : suites) {
     73       outSuites.add(s);
     74 //      calculateAllSuites(s.getChildSuites(), outSuites);
     75     }
     76   }
     77 
     78   @Override
     79   public void run() {
     80     IMessageSender sender = m_serPort != null
     81         ? new SerializedMessageSender(m_host, m_serPort, m_ack)
     82         : new StringMessageSender(m_host, m_port);
     83     final MessageHub msh = new MessageHub(sender);
     84     msh.setDebug(isDebug());
     85     try {
     86       msh.connect();
     87       // We couldn't do this until now in debug mode since the .xml file didn't exist yet.
     88       // Now that we have connected with the Eclipse client, we know that it created the .xml
     89       // file so we can proceed with the initialization
     90       initializeSuitesAndJarFile();
     91 
     92       List<XmlSuite> suites = Lists.newArrayList();
     93       calculateAllSuites(m_suites, suites);
     94 //      System.out.println("Suites: " + m_suites.get(0).getChildSuites().size()
     95 //          + " and:" + suites.get(0).getChildSuites().size());
     96       if(suites.size() > 0) {
     97 
     98         int testCount= 0;
     99 
    100         for (XmlSuite suite : suites) {
    101           testCount += suite.getTests().size();
    102         }
    103 
    104         GenericMessage gm= new GenericMessage(MessageHelper.GENERIC_SUITE_COUNT);
    105         gm.setSuiteCount(suites.size());
    106         gm.setTestCount(testCount);
    107         msh.sendMessage(gm);
    108 
    109         addListener(new RemoteSuiteListener(msh));
    110         setTestRunnerFactory(new DelegatingTestRunnerFactory(buildTestRunnerFactory(), msh));
    111 
    112 //        System.out.println("RemoteTestNG starting");
    113         super.run();
    114       }
    115       else {
    116         System.err.println("No test suite found. Nothing to run");
    117       }
    118     }
    119     catch(Throwable cause) {
    120       cause.printStackTrace(System.err);
    121     }
    122     finally {
    123 //      System.out.println("RemoteTestNG finishing: " + (getEnd() - getStart()) + " ms");
    124       msh.shutDown();
    125       if (! m_debug && ! m_dontExit) {
    126         System.exit(0);
    127       }
    128     }
    129   }
    130 
    131   /**
    132    * Override by the plugin if you need to configure differently the <code>TestRunner</code>
    133    * (usually this is needed if different listeners/reporters are needed).
    134    * <b>Note</b>: you don't need to worry about the wiring listener, because it is added
    135    * automatically.
    136    */
    137   protected ITestRunnerFactory buildTestRunnerFactory() {
    138     if(null == m_customTestRunnerFactory) {
    139       m_customTestRunnerFactory= new ITestRunnerFactory() {
    140           @Override
    141           public TestRunner newTestRunner(ISuite suite, XmlTest xmlTest,
    142               Collection<IInvokedMethodListener> listeners, List<IClassListener> classListeners) {
    143             TestRunner runner =
    144               new TestRunner(getConfiguration(), suite, xmlTest,
    145                   false /*skipFailedInvocationCounts */,
    146                   listeners, classListeners);
    147             if (m_useDefaultListeners) {
    148               runner.addListener(new TestHTMLReporter());
    149               runner.addListener(new JUnitXMLReporter());
    150             }
    151 
    152             return runner;
    153           }
    154         };
    155     }
    156 
    157     return m_customTestRunnerFactory;
    158   }
    159 
    160   public static void main(String[] args) throws ParameterException {
    161     CommandLineArgs cla = new CommandLineArgs();
    162     RemoteArgs ra = new RemoteArgs();
    163     new JCommander(Arrays.asList(cla, ra), args);
    164     m_dontExit = ra.dontExit;
    165     if (cla.port != null && ra.serPort != null) {
    166       throw new TestNGException("Can only specify one of " + CommandLineArgs.PORT
    167           + " and " + RemoteArgs.PORT);
    168     }
    169     m_debug = cla.debug;
    170     m_ack = ra.ack;
    171     if (m_debug) {
    172 //      while (true) {
    173         initAndRun(args, cla, ra);
    174 //      }
    175     }
    176     else {
    177       initAndRun(args, cla, ra);
    178     }
    179   }
    180 
    181   private static void initAndRun(String[] args, CommandLineArgs cla, RemoteArgs ra) {
    182     RemoteTestNG remoteTestNg = new RemoteTestNG();
    183     if (m_debug) {
    184       // In debug mode, override the port and the XML file to a fixed location
    185       cla.port = Integer.parseInt(DEBUG_PORT);
    186       ra.serPort = cla.port;
    187       cla.suiteFiles = Arrays.asList(new String[] {
    188           DEBUG_SUITE_DIRECTORY + DEBUG_SUITE_FILE
    189       });
    190     }
    191     remoteTestNg.configure(cla);
    192     remoteTestNg.setHost(cla.host);
    193     m_serPort = ra.serPort;
    194     remoteTestNg.m_port = cla.port;
    195     if (isVerbose()) {
    196       StringBuilder sb = new StringBuilder("Invoked with ");
    197       for (String s : args) {
    198         sb.append(s).append(" ");
    199       }
    200       p(sb.toString());
    201 //      remoteTestNg.setVerbose(1);
    202 //    } else {
    203 //      remoteTestNg.setVerbose(0);
    204     }
    205     validateCommandLineParameters(cla);
    206     remoteTestNg.run();
    207 //    if (m_debug) {
    208 //      // Run in a loop if in debug mode so it is possible to run several launches
    209 //      // without having to relauch RemoteTestNG.
    210 //      while (true) {
    211 //        remoteTestNg.run();
    212 //        remoteTestNg.configure(cla);
    213 //      }
    214 //    } else {
    215 //      remoteTestNg.run();
    216 //    }
    217   }
    218 
    219   private static void p(String s) {
    220     if (isVerbose()) {
    221       System.out.println("[RemoteTestNG] " + s);
    222     }
    223   }
    224 
    225   public static boolean isVerbose() {
    226     boolean result = System.getProperty(PROPERTY_VERBOSE) != null || isDebug();
    227     return result;
    228   }
    229 
    230   public static boolean isDebug() {
    231     return m_debug || System.getProperty(PROPERTY_DEBUG) != null;
    232   }
    233 
    234   private String getHost() {
    235     return m_host;
    236   }
    237 
    238   private int getPort() {
    239     return m_port;
    240   }
    241 
    242   /** A ISuiteListener wiring the results using the internal string-based protocol. */
    243   private static class RemoteSuiteListener implements ISuiteListener {
    244     private final MessageHub m_messageSender;
    245 
    246     RemoteSuiteListener(MessageHub smsh) {
    247       m_messageSender= smsh;
    248     }
    249 
    250     @Override
    251     public void onFinish(ISuite suite) {
    252       m_messageSender.sendMessage(new SuiteMessage(suite, false /*start*/));
    253     }
    254 
    255     @Override
    256     public void onStart(ISuite suite) {
    257       m_messageSender.sendMessage(new SuiteMessage(suite, true /*start*/));
    258     }
    259   }
    260 
    261   private static class DelegatingTestRunnerFactory implements ITestRunnerFactory {
    262     private final ITestRunnerFactory m_delegateFactory;
    263     private final MessageHub m_messageSender;
    264 
    265     DelegatingTestRunnerFactory(ITestRunnerFactory trf, MessageHub smsh) {
    266       m_delegateFactory= trf;
    267       m_messageSender= smsh;
    268     }
    269 
    270     @Override
    271     public TestRunner newTestRunner(ISuite suite, XmlTest test,
    272         Collection<IInvokedMethodListener> listeners, List<IClassListener> classListeners) {
    273       TestRunner tr = m_delegateFactory.newTestRunner(suite, test, listeners, classListeners);
    274       tr.addListener(new RemoteTestListener(suite, test, m_messageSender));
    275       return tr;
    276     }
    277   }
    278 }
    279