天天看點

surefire 拉起testng單元測試類的源碼流程閱讀(一)

這裡分析是基于2.5surefire 版本。

首先拿surefire 拉起單元測試報錯日志 分析:

Caused by: java.io.IOException: Error while instrumenting class com/suning/gcps/newutils/string/StringUtils.
	at org.jacoco.agent.rt.internal_6da5971.core.instr.Instrumenter.instrumentError(Instrumenter.java:160)
	at org.jacoco.agent.rt.internal_6da5971.core.instr.Instrumenter.instrument(Instrumenter.java:111)
	at org.jacoco.agent.rt.internal_6da5971.CoverageTransformer.transform(CoverageTransformer.java:91)
	... 46 more
Caused by: java.lang.IllegalStateException: Class com/suning/gcps/newutils/string/StringUtils is already instrumented.
	at org.jacoco.agent.rt.internal_6da5971.core.internal.instr.InstrSupport.assertNotInstrumented(InstrSupport.java:89)
	at org.jacoco.agent.rt.internal_6da5971.core.internal.instr.ClassInstrumenter.visitField(ClassInstrumenter.java:55)
	at org.jacoco.agent.rt.internal_6da5971.asm.ClassVisitor.visitField(ClassVisitor.java:272)
	at org.jacoco.agent.rt.internal_6da5971.asm.ClassReader.readField(ClassReader.java:768)
	at org.jacoco.agent.rt.internal_6da5971.asm.ClassReader.accept(ClassReader.java:689)
	at org.jacoco.agent.rt.internal_6da5971.asm.ClassReader.accept(ClassReader.java:506)
	at org.jacoco.agent.rt.internal_6da5971.core.instr.Instrumenter.instrument(Instrumenter.java:84)
	at org.jacoco.agent.rt.internal_6da5971.core.instr.Instrumenter.instrument(Instrumenter.java:108)
	... 47 more
11:38:21.536 [main] ERROR c.s.gcps.newutils.string.StringUtils - 資料格式轉換錯誤!3w
java.lang.NumberFormatException: For input string: "3w"
	at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65) ~[na:1.7.0_75]
	at java.lang.Integer.parseInt(Integer.java:492) ~[na:1.7.0_75]
	at java.lang.Integer.parseInt(Integer.java:527) ~[na:1.7.0_75]
	at com.suning.gcps.newutils.string.StringUtils.isIntString(StringUtils.java:216) ~[classes/:na]
	at com.suning.gcps.newutils.string.StringUtilsTest.test(StringUtilsTest.java:33) [test-classes/:na]
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.7.0_75]
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) ~[na:1.7.0_75]
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.7.0_75]
	at java.lang.reflect.Method.invoke(Method.java:606) ~[na:1.7.0_75]
	at org.testng.internal.MethodInvocationHelper.invokeMethod(MethodInvocationHelper.java:100) [testng-6.9.12.jar:na]
	at org.testng.internal.Invoker.invokeMethod(Invoker.java:646) [testng-6.9.12.jar:na]
	at org.testng.internal.Invoker.invokeTestMethod(Invoker.java:811) [testng-6.9.12.jar:na]
	at org.testng.internal.Invoker.invokeTestMethods(Invoker.java:1129) [testng-6.9.12.jar:na]
	at org.testng.internal.TestMethodWorker.invokeTestMethods(TestMethodWorker.java:129) [testng-6.9.12.jar:na]
	at org.testng.internal.TestMethodWorker.run(TestMethodWorker.java:112) [testng-6.9.12.jar:na]
	at org.testng.TestRunner.privateRun(TestRunner.java:746) [testng-6.9.12.jar:na]
	at org.testng.TestRunner.run(TestRunner.java:600) [testng-6.9.12.jar:na]
	at org.testng.SuiteRunner.runTest(SuiteRunner.java:366) [testng-6.9.12.jar:na]
	at org.testng.SuiteRunner.runSequentially(SuiteRunner.java:361) [testng-6.9.12.jar:na]
	at org.testng.SuiteRunner.privateRun(SuiteRunner.java:319) [testng-6.9.12.jar:na]
	at org.testng.SuiteRunner.run(SuiteRunner.java:268) [testng-6.9.12.jar:na]
	at org.testng.SuiteRunnerWorker.runSuite(SuiteRunnerWorker.java:52) [testng-6.9.12.jar:na]
	at org.testng.SuiteRunnerWorker.run(SuiteRunnerWorker.java:86) [testng-6.9.12.jar:na]
	at org.testng.TestNG.runSuitesSequentially(TestNG.java:1264) [testng-6.9.12.jar:na]
	at org.testng.TestNG.runSuitesLocally(TestNG.java:1189) [testng-6.9.12.jar:na]
	at org.testng.TestNG.runSuites(TestNG.java:1104) [testng-6.9.12.jar:na]
	at org.testng.TestNG.run(TestNG.java:1076) [testng-6.9.12.jar:na]
	at org.apache.maven.surefire.testng.TestNGExecutor.run(TestNGExecutor.java:62) [surefire-testng-2.5.jar:2.5]
	at org.apache.maven.surefire.testng.TestNGDirectoryTestSuite.execute(TestNGDirectoryTestSuite.java:141) [surefire-testng-2.5.jar:2.5]
	at org.apache.maven.surefire.Surefire.run(Surefire.java:180) [surefire-api-2.5.jar:2.5]
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.7.0_75]
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) ~[na:1.7.0_75]
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.7.0_75]
	at java.lang.reflect.Method.invoke(Method.java:606) ~[na:1.7.0_75]
	at org.apache.maven.surefire.booter.SurefireBooter.runSuitesInProcess(SurefireBooter.java:350) [surefire-booter-2.5.jar:2.5]
	at org.apache.maven.surefire.booter.SurefireBooter.main(SurefireBooter.java:1021) [surefire-booter-2.5.jar:2.5]
           

上述報錯日志展示 testng使用6.9.12 ,surefire使用的2.5版本。

-> SuiteRunnerWorker.run -> runSuites

-> SuiteRunner.run -> privateRun -> invokeTestMethods 

->testng.internal.TestMethodWorker.run -> invokeTestMethods

->testng.internal.Invoker.invokeTestMethods -> invokeMethod

->testng.internal.MethodInvocationHelper.invokeMethod

然後surefire 通過調用 SurefireBooter.main(surefire-booter.jar) -> SurefireBooter.runSuitesInProcess

-> maven.surefire.Surefire.run(surefire-api-2.5.jar)

-> testng.TestNGExecutor.run(surefire-testng-2.5)

-> TestNG.run(testng-6.9.12)

SurefireBooter 代碼:

static
  {
    try
    {
      assertionStatusMethod = class$java$lang$ClassLoader.getMethod("setDefaultAssertionStatus", new Class[] { Boolean.TYPE });
    }
    catch (NoSuchMethodException e)
    {
      assertionStatusMethod = null;
    }
  }
  
  public SurefireBooter()
  {
    this.isForked = false;
  }
  
  private SurefireBooter(boolean isForked)
  {
    this.isForked = isForked;
  }
  
  public void addReport(String report)
  {
    addReport(report, null);
  }
  
  public void addReport(String report, Object[] constructorParams)
  {
    this.reports.add(new Object[] { report, constructorParams });
  }
  
  public void addTestSuite(String suiteClassName, Object[] constructorParams)
  {
    this.testSuites.add(new Object[] { suiteClassName, constructorParams });
  }
  
  public void addClassPathUrl(String path)
  {
    if (!this.classPathUrls.contains(path)) {
      this.classPathUrls.add(path);
    }
  }
  
  public void addSurefireClassPathUrl(String path)
  {
    if (!this.surefireClassPathUrls.contains(path)) {
      this.surefireClassPathUrls.add(path);
    }
  }
  
  public void addSurefireBootClassPathUrl(String path)
  {
    if (!this.surefireBootClassPathUrls.contains(path)) {
      this.surefireBootClassPathUrls.add(path);
    }
  }
  
  public void setFailIfNoTests(boolean failIfNoTests)
  {
    this.failIfNoTests = failIfNoTests;
  }
  
  public void setRedirectTestOutputToFile(boolean redirectTestOutputToFile)
  {
    this.redirectTestOutputToFile = redirectTestOutputToFile;
  }
  
  public void setReportsDirectory(File reportsDirectory)
  {
    this.reportsDirectory = reportsDirectory;
  }
  
  public File getReportsDirectory()
  {
    return this.reportsDirectory;
  }
  
  public void setForkConfiguration(ForkConfiguration forkConfiguration)
  {
    this.forkConfiguration = forkConfiguration;
  }
  
  public boolean isForking()
  {
    return this.forkConfiguration.isForking();
  }
  
  public int run()
    throws SurefireBooterForkException, SurefireExecutionException
  {
    int result;
    if ("never".equals(this.forkConfiguration.getForkMode()))
    {
      result = runSuitesInProcess();
    }
    else
    {
      int result;
      if ("once".equals(this.forkConfiguration.getForkMode()))
      {
        result = runSuitesForkOnce();
      }
      else
      {
        int result;
        if ("always".equals(this.forkConfiguration.getForkMode())) {
          result = runSuitesForkPerTestSet();
        } else {
          throw new SurefireExecutionException("Unknown forkmode: " + this.forkConfiguration.getForkMode(), null);
        }
      }
    }
    int result;
    return result;
  }
  
  private int runSuitesInProcess(String testSet, Properties results)
    throws SurefireExecutionException
  {
    if (this.testSuites.size() != 1) {
      throw new IllegalArgumentException("Cannot only specify testSet for single test suites");
    }
    ClassLoader oldContextClassLoader = Thread.currentThread().getContextClassLoader();
    try
    {
      ClassLoader testsClassLoader = useSystemClassLoader() ? ClassLoader.getSystemClassLoader() : createClassLoader(this.classPathUrls, null, this.childDelegation);
      



      ClassLoader surefireClassLoader = createClassLoader(this.surefireClassPathUrls, testsClassLoader);
      
      Class surefireClass = surefireClassLoader.loadClass(Surefire.class.getName());
      
      Object surefire = surefireClass.newInstance();
      
      Method run = surefireClass.getMethod("run", new Class[] { List.class, new Object[0].getClass(), String.class, ClassLoader.class, ClassLoader.class, Properties.class, Boolean.class });
      


      Thread.currentThread().setContextClassLoader(testsClassLoader);
      
      Integer result = (Integer)run.invoke(surefire, new Object[] { this.reports, this.testSuites.get(0), testSet, surefireClassLoader, testsClassLoader, results, new Boolean(this.failIfNoTests) });
      


      return result.intValue();
    }
    catch (InvocationTargetException e)
    {
      throw new SurefireExecutionException(e.getTargetException().getMessage(), e.getTargetException());
    }
    catch (Exception e)
    {
      throw new SurefireExecutionException("Unable to instantiate and execute Surefire", e);
    }
    finally
    {
      Thread.currentThread().setContextClassLoader(oldContextClassLoader);
    }
  }
  
  private int runSuitesInProcess()
    throws SurefireExecutionException
  {
    ClassLoader oldContextClassLoader = Thread.currentThread().getContextClassLoader();
    try
    {
      String testClassPath = getTestClassPathAsString();
      System.setProperty("surefire.test.class.path", testClassPath);
      ClassLoader testsClassLoader;
      if (useManifestOnlyJar())
      {
        ClassLoader testsClassLoader = getClass().getClassLoader();
        

        System.setProperty("surefire.real.class.path", System.getProperty("java.class.path"));
        System.setProperty("java.class.path", testClassPath);
      }
      else
      {
        testsClassLoader = createClassLoader(this.classPathUrls, null, this.childDelegation);
      }
      ClassLoader surefireClassLoader = createClassLoader(this.surefireClassPathUrls, testsClassLoader);
      
      Class surefireClass = surefireClassLoader.loadClass(Surefire.class.getName());
      
      Object surefire = surefireClass.newInstance();
      
      Method run = surefireClass.getMethod("run", new Class[] { List.class, List.class, ClassLoader.class, ClassLoader.class, Boolean.class });
      


      Thread.currentThread().setContextClassLoader(testsClassLoader);
      
      Integer result = (Integer)run.invoke(surefire, new Object[] { this.reports, this.testSuites, surefireClassLoader, testsClassLoader, new Boolean(this.failIfNoTests) });
      


      return result.intValue();
    }
    catch (InvocationTargetException e)
    {
      throw new SurefireExecutionException(e.getTargetException().getMessage(), e.getTargetException());
    }
    catch (Exception e)
    {
      throw new SurefireExecutionException("Unable to instantiate and execute Surefire", e);
    }
    finally
    {
      Thread.currentThread().setContextClassLoader(oldContextClassLoader);
    }
  }
  
  private String getTestClassPathAsString()
  {
    StringBuffer sb = new StringBuffer();
    for (int i = 0; i < this.classPathUrls.size(); i++) {
      sb.append(this.classPathUrls.get(i)).append(File.pathSeparatorChar);
    }
    return sb.toString();
  }
  
  private int runSuitesForkOnce()
    throws SurefireBooterForkException
  {
    return forkSuites(this.testSuites, true, true);
  }
  
  private int runSuitesForkPerTestSet()
    throws SurefireBooterForkException
  {
    ClassLoader testsClassLoader;
    ClassLoader surefireClassLoader;
    try
    {
      testsClassLoader = createClassLoader(this.classPathUrls, null, false);
      
      surefireClassLoader = createClassLoader(this.surefireClassPathUrls, testsClassLoader, false);
    }
    catch (MalformedURLException e)
    {
      throw new SurefireBooterForkException("Unable to create classloader to find test suites", e);
    }
    int globalResult = 0;
    
    boolean showHeading = true;
    Properties properties = new Properties();
    for (Iterator i = this.testSuites.iterator(); i.hasNext();)
    {
      testSuite = (Object[])i.next();
      
      Map testSets = getTestSets(testSuite, testsClassLoader, surefireClassLoader);
      for (j = testSets.keySet().iterator(); j.hasNext();)
      {
        Object testSet = j.next();
        boolean showFooter = (!j.hasNext()) && (!i.hasNext());
        int result = forkSuite(testSuite, testSet, showHeading, showFooter, properties);
        if (result > globalResult) {
          globalResult = result;
        }
        showHeading = false;
      }
    }
    Object[] testSuite;
    Iterator j;
    return globalResult;
  }
  
  private Map getTestSets(Object[] testSuite, ClassLoader testsClassLoader, ClassLoader surefireClassLoader)
    throws SurefireBooterForkException
  {
    String className = (String)testSuite[0];
    
    Object[] params = (Object[])testSuite[1];
    Object suite;
    try
    {
      suite = Surefire.instantiateObject(className, params, surefireClassLoader);
    }
    catch (TestSetFailedException e)
    {
      throw new SurefireBooterForkException(e.getMessage(), e.getCause());
    }
    catch (ClassNotFoundException e)
    {
      throw new SurefireBooterForkException("Unable to find class for test suite '" + className + "'", e);
    }
    catch (NoSuchMethodException e)
    {
      throw new SurefireBooterForkException("Unable to find appropriate constructor for test suite '" + className + "': " + e.getMessage(), e);
    }
    Map testSets;
    try
    {
      Method m = suite.getClass().getMethod("locateTestSets", new Class[] { ClassLoader.class });
      
      testSets = (Map)m.invoke(suite, new Object[] { testsClassLoader });
    }
    catch (IllegalAccessException e)
    {
      throw new SurefireBooterForkException("Error obtaining test sets", e);
    }
    catch (NoSuchMethodException e)
    {
      throw new SurefireBooterForkException("Error obtaining test sets", e);
    }
    catch (InvocationTargetException e)
    {
      throw new SurefireBooterForkException(e.getTargetException().getMessage(), e.getTargetException());
    }
    return testSets;
  }
  
  private int forkSuites(List testSuites, boolean showHeading, boolean showFooter)
    throws SurefireBooterForkException
  {
    Properties properties = new Properties();
    
    setForkProperties(testSuites, properties);
    
    return fork(properties, showHeading, showFooter);
  }
  
  private int forkSuite(Object[] testSuite, Object testSet, boolean showHeading, boolean showFooter, Properties properties)
    throws SurefireBooterForkException
  {
    setForkProperties(Collections.singletonList(testSuite), properties);
    if ((testSet instanceof String)) {
      properties.setProperty("testSet", (String)testSet);
    }
    return fork(properties, showHeading, showFooter);
  }
  
  private void setForkProperties(List testSuites, Properties properties)
  {
    addPropertiesForTypeHolder(this.reports, properties, "report.");
    addPropertiesForTypeHolder(testSuites, properties, "testSuite.");
    for (int i = 0; i < this.classPathUrls.size(); i++)
    {
      String url = (String)this.classPathUrls.get(i);
      properties.setProperty("classPathUrl." + i, url);
    }
    for (int i = 0; i < this.surefireClassPathUrls.size(); i++)
    {
      String url = (String)this.surefireClassPathUrls.get(i);
      properties.setProperty("surefireClassPathUrl." + i, url);
    }
    properties.setProperty("childDelegation", String.valueOf(this.childDelegation));
    properties.setProperty("enableAssertions", String.valueOf(this.enableAssertions));
    properties.setProperty("useSystemClassLoader", String.valueOf(useSystemClassLoader()));
    properties.setProperty("useManifestOnlyJar", String.valueOf(useManifestOnlyJar()));
    properties.setProperty("failIfNoTests", String.valueOf(this.failIfNoTests));
  }
  
  private File writePropertiesFile(String name, Properties properties)
    throws IOException
  {
    File file = File.createTempFile(name, "tmp");
    if (!this.forkConfiguration.isDebug()) {
      file.deleteOnExit();
    }
    writePropertiesFile(file, name, properties);
    
    return file;
  }
  
  private void writePropertiesFile(File file, String name, Properties properties)
    throws IOException
  {
    FileOutputStream out = new FileOutputStream(file);
    try
    {
      properties.store(out, name);
    }
    finally
    {
      IOUtil.close(out);
    }
  }
  
  private void addPropertiesForTypeHolder(List typeHolderList, Properties properties, String propertyPrefix)
  {
    for (int i = 0; i < typeHolderList.size(); i++)
    {
      Object[] report = (Object[])typeHolderList.get(i);
      
      String className = (String)report[0];
      Object[] params = (Object[])report[1];
      
      properties.setProperty(propertyPrefix + i, className);
      if (params != null)
      {
        String paramProperty = convert(params[0]);
        String typeProperty = params[0].getClass().getName();
        for (int j = 1; j < params.length; j++)
        {
          paramProperty = paramProperty + "|";
          typeProperty = typeProperty + "|";
          if (params[j] != null)
          {
            paramProperty = paramProperty + convert(params[j]);
            typeProperty = typeProperty + params[j].getClass().getName();
          }
        }
        properties.setProperty(propertyPrefix + i + ".params", paramProperty);
        properties.setProperty(propertyPrefix + i + ".types", typeProperty);
      }
    }
  }
  
  private static String convert(Object param)
  {
    if ((param instanceof File[]))
    {
      File[] files = (File[])param;
      return "[" + StringUtils.join(files, ",") + "]";
    }
    if ((param instanceof Properties))
    {
      ByteArrayOutputStream baos = new ByteArrayOutputStream();
      try
      {
        ((Properties)param).store(baos, "");
        return new String(baos.toByteArray(), "8859_1");
      }
      catch (Exception e)
      {
        throw new RuntimeException("bug in property conversion", e);
      }
    }
    return param.toString();
  }
  
  private boolean useSystemClassLoader()
  {
    return (this.forkConfiguration.isUseSystemClassLoader()) && ((this.isForked) || (this.forkConfiguration.isForking()));
  }
  
  private boolean useManifestOnlyJar()
  {
    return (this.forkConfiguration.isUseSystemClassLoader()) && (this.forkConfiguration.isUseManifestOnlyJar());
  }
  
  private int fork(Properties properties, boolean showHeading, boolean showFooter)
    throws SurefireBooterForkException
  {
    File systemProperties = null;
    File surefireProperties;
    try
    {
      surefireProperties = writePropertiesFile("surefire", properties);
      if (this.forkConfiguration.getSystemProperties() != null) {
        systemProperties = writePropertiesFile("surefire", this.forkConfiguration.getSystemProperties());
      }
    }
    catch (IOException e)
    {
      throw new SurefireBooterForkException("Error creating properties files for forking", e);
    }
    List bootClasspath = new ArrayList(this.surefireBootClassPathUrls.size() + this.classPathUrls.size());
    
    bootClasspath.addAll(this.surefireBootClassPathUrls);
    if (useSystemClassLoader()) {
      bootClasspath.addAll(this.classPathUrls);
    }
    Commandline cli = this.forkConfiguration.createCommandLine(bootClasspath, useManifestOnlyJar());
    
    cli.createArg().setFile(surefireProperties);
    if (systemProperties != null) {
      cli.createArg().setFile(systemProperties);
    }
    ForkingStreamConsumer out = getForkingStreamConsumer(showHeading, showFooter, this.redirectTestOutputToFile);
    StreamConsumer err;
    StreamConsumer err;
    if (this.redirectTestOutputToFile) {
      err = out;
    } else {
      err = getForkingStreamConsumer(showHeading, showFooter, this.redirectTestOutputToFile);
    }
    if (this.forkConfiguration.isDebug()) {
      System.out.println("Forking command line: " + cli);
    }
    int returnCode;
    try
    {
      returnCode = CommandLineUtils.executeCommandLine(cli, out, err, this.forkedProcessTimeoutInSeconds);
    }
    catch (CommandLineException e)
    {
      throw new SurefireBooterForkException("Error while executing forked tests.", e);
    }
    if (this.redirectTestOutputToFile) {
      try
      {
        out.getOutputConsumer().testSetCompleted();
      }
      catch (Exception e) {}
    }
    if ((surefireProperties != null) && (surefireProperties.exists()))
    {
      FileInputStream inStream = null;
      try
      {
        inStream = new FileInputStream(surefireProperties);
        
        properties.load(inStream);
      }
      catch (FileNotFoundException e)
      {
        throw new SurefireBooterForkException("Unable to reload properties file from forked process", e);
      }
      catch (IOException e)
      {
        throw new SurefireBooterForkException("Unable to reload properties file from forked process", e);
      }
      finally
      {
        IOUtil.close(inStream);
      }
    }
    return returnCode;
  }
  
  private ClassLoader createClassLoader(List classPathUrls, ClassLoader parent)
    throws MalformedURLException
  {
    return createClassLoader(classPathUrls, parent, false);
  }
  
  private ClassLoader createClassLoader(List classPathUrls, ClassLoader parent, boolean childDelegation)
    throws MalformedURLException
  {
    List urls = new ArrayList();
    for (Iterator i = classPathUrls.iterator(); i.hasNext();)
    {
      String url = (String)i.next();
      if (url != null)
      {
        File f = new File(url);
        urls.add(UrlUtils.getURL(f));
      }
    }
    IsolatedClassLoader classLoader = new IsolatedClassLoader(parent, childDelegation);
    if (assertionStatusMethod != null) {
      try
      {
        Object[] args = { this.enableAssertions ? Boolean.TRUE : Boolean.FALSE };
        if (parent != null) {
          assertionStatusMethod.invoke(parent, args);
        }
        assertionStatusMethod.invoke(classLoader, args);
      }
      catch (IllegalAccessException e)
      {
        throw new NestedRuntimeException("Unable to access the assertion enablement method", e);
      }
      catch (InvocationTargetException e)
      {
        throw new NestedRuntimeException("Unable to invoke the assertion enablement method", e);
      }
    }
    for (Iterator iter = urls.iterator(); iter.hasNext();)
    {
      URL url = (URL)iter.next();
      classLoader.addURL(url);
    }
    return classLoader;
  }
  
  private static List processStringList(String stringList)
  {
    String sl = stringList;
    if ((sl.startsWith("[")) && (sl.endsWith("]"))) {
      sl = sl.substring(1, sl.length() - 1);
    }
    List list = new ArrayList();
    
    String[] stringArray = StringUtils.split(sl, ",");
    for (int i = 0; i < stringArray.length; i++) {
      list.add(stringArray[i].trim());
    }
    return list;
  }
  
  private static Properties loadProperties(File file)
    throws IOException
  {
    Properties p = new Properties();
    if ((file != null) && (file.exists()))
    {
      FileInputStream inStream = new FileInputStream(file);
      try
      {
        p.load(inStream);
      }
      finally
      {
        IOUtil.close(inStream);
      }
    }
    return p;
  }
  
  private static void setSystemProperties(File file)
    throws IOException
  {
    Properties p = loadProperties(file);
    for (Iterator i = p.keySet().iterator(); i.hasNext();)
    {
      String key = (String)i.next();
      
      System.setProperty(key, p.getProperty(key));
    }
  }
  
  private static Object[] constructParamObjects(String paramProperty, String typeProperty)
  {
    Object[] paramObjects = null;
    if (paramProperty != null)
    {
      String[] params = StringUtils.split(StringUtils.replace(StringUtils.replace(paramProperty, "||", "| |"), "||", "| |"), "|");
      

      String[] types = StringUtils.split(StringUtils.replace(StringUtils.replace(typeProperty, "||", "| |"), "||", "| |"), "|");
      


      paramObjects = new Object[params.length];
      for (int i = 0; i < types.length; i++) {
        if (types[i].trim().length() == 0)
        {
          params[i] = null;
        }
        else if (types[i].equals(String.class.getName()))
        {
          paramObjects[i] = params[i];
        }
        else if (types[i].equals(File.class.getName()))
        {
          paramObjects[i] = new File(params[i]);
        }
        else if (types[i].equals(new File[0].getClass().getName()))
        {
          List stringList = processStringList(params[i]);
          File[] fileList = new File[stringList.size()];
          for (int j = 0; j < stringList.size(); j++) {
            fileList[j] = new File((String)stringList.get(j));
          }
          paramObjects[i] = fileList;
        }
        else if (types[i].equals(ArrayList.class.getName()))
        {
          paramObjects[i] = processStringList(params[i]);
        }
        else if (types[i].equals(Boolean.class.getName()))
        {
          paramObjects[i] = Boolean.valueOf(params[i]);
        }
        else if (types[i].equals(Integer.class.getName()))
        {
          paramObjects[i] = Integer.valueOf(params[i]);
        }
        else if (types[i].equals(Properties.class.getName()))
        {
          Properties result = new Properties();
          String value = params[i];
          try
          {
            ByteArrayInputStream bais = new ByteArrayInputStream(value.getBytes("8859_1"));
            result.load(bais);
          }
          catch (Exception e)
          {
            throw new RuntimeException("bug in property conversion", e);
          }
          paramObjects[i] = result;
        }
        else
        {
          throw new IllegalArgumentException("Unknown parameter type: " + types[i]);
        }
      }
    }
    return paramObjects;
  }
  
  public static void main(String[] args)
    throws Throwable
  {
    try
    {
      if (args.length > 1) {
        setSystemProperties(new File(args[1]));
      }
      File surefirePropertiesFile = new File(args[0]);
      Properties p = loadProperties(surefirePropertiesFile);
      
      SortedMap classPathUrls = new TreeMap();
      
      SortedMap surefireClassPathUrls = new TreeMap();
      
      SurefireBooter surefireBooter = new SurefireBooter(true);
      
      ForkConfiguration forkConfiguration = new ForkConfiguration();
      forkConfiguration.setForkMode("never");
      surefireBooter.setForkConfiguration(forkConfiguration);
      for (Enumeration e = p.propertyNames(); e.hasMoreElements();)
      {
        String name = (String)e.nextElement();
        if ((name.startsWith("report.")) && (!name.endsWith(".params")) && (!name.endsWith(".types")))
        {
          String className = p.getProperty(name);
          
          String params = p.getProperty(name + ".params");
          String types = p.getProperty(name + ".types");
          surefireBooter.addReport(className, constructParamObjects(params, types));
        }
        else if ((name.startsWith("testSuite.")) && (!name.endsWith(".params")) && (!name.endsWith(".types")))
        {
          String className = p.getProperty(name);
          
          String params = p.getProperty(name + ".params");
          String types = p.getProperty(name + ".types");
          surefireBooter.addTestSuite(className, constructParamObjects(params, types));
        }
        else if (name.startsWith("classPathUrl."))
        {
          classPathUrls.put(Integer.valueOf(name.substring(name.indexOf('.') + 1)), p.getProperty(name));
        }
        else if (name.startsWith("surefireClassPathUrl."))
        {
          surefireClassPathUrls.put(Integer.valueOf(name.substring(name.indexOf('.') + 1)), p.getProperty(name));
        }
        else if (name.startsWith("surefireBootClassPathUrl."))
        {
          surefireBooter.addSurefireBootClassPathUrl(p.getProperty(name));
        }
        else if ("childDelegation".equals(name))
        {
          surefireBooter.childDelegation = Boolean.valueOf(p.getProperty("childDelegation")).booleanValue();
        }
        else if ("enableAssertions".equals(name))
        {
          surefireBooter.enableAssertions = Boolean.valueOf(p.getProperty("enableAssertions")).booleanValue();
        }
        else if ("useSystemClassLoader".equals(name))
        {
          boolean value = Boolean.valueOf(p.getProperty("useSystemClassLoader")).booleanValue();
          surefireBooter.forkConfiguration.setUseSystemClassLoader(value);
        }
        else if ("useManifestOnlyJar".equals(name))
        {
          boolean value = Boolean.valueOf(p.getProperty("useManifestOnlyJar")).booleanValue();
          surefireBooter.forkConfiguration.setUseManifestOnlyJar(value);
        }
        else if ("failIfNoTests".equals(name))
        {
          boolean value = Boolean.valueOf(p.getProperty("failIfNoTests")).booleanValue();
          surefireBooter.setFailIfNoTests(value);
        }
      }
      for (Iterator cpi = classPathUrls.keySet().iterator(); cpi.hasNext();)
      {
        String url = (String)classPathUrls.get(cpi.next());
        surefireBooter.addClassPathUrl(url);
      }
      for (Iterator scpi = surefireClassPathUrls.keySet().iterator(); scpi.hasNext();)
      {
        String url = (String)surefireClassPathUrls.get(scpi.next());
        surefireBooter.addSurefireClassPathUrl(url);
      }
      String testSet = p.getProperty("testSet");
      int result;
      int result;
      if (testSet != null) {
        result = surefireBooter.runSuitesInProcess(testSet, p);
      } else {
        result = surefireBooter.runSuitesInProcess();
      }
      surefireBooter.writePropertiesFile(surefirePropertiesFile, "surefire", p);
      

      System.exit(result);
    }
    catch (Throwable t)
    {
      t.printStackTrace(System.err);
      
      System.exit(1);
    }
  }
  
  public void setChildDelegation(boolean childDelegation)
  {
    this.childDelegation = childDelegation;
  }
  
  private ForkingStreamConsumer getForkingStreamConsumer(boolean showHeading, boolean showFooter, boolean redirectTestOutputToFile)
  {
    OutputConsumer outputConsumer = new StandardOutputConsumer();
    if (redirectTestOutputToFile) {
      outputConsumer = new FileOutputConsumerProxy(outputConsumer, getReportsDirectory());
    }
    if (!showHeading) {
      outputConsumer = new SupressHeaderOutputConsumerProxy(outputConsumer);
    }
    if (!showFooter) {
      outputConsumer = new SupressFooterOutputConsumerProxy(outputConsumer);
    }
    return new ForkingStreamConsumer(outputConsumer);
  }
  
  public void setEnableAssertions(boolean enableAssertions)
  {
    this.enableAssertions = enableAssertions;
  }
  
  public void setForkedProcessTimeoutInSeconds(int forkedProcessTimeoutInSeconds)
  {
    this.forkedProcessTimeoutInSeconds = forkedProcessTimeoutInSeconds;
  }
           

TestNGExecutor類代碼:

public class TestNGExecutor
{
  public static void run(Class[] testClasses, String testSourceDirectory, Map options, ArtifactVersion version, String classifier, ReporterManager reportManager, SurefireTestSuite suite, File reportsDirectory)
    throws TestSetFailedException
  {
    TestNG testng = new TestNG(true);
    Configurator configurator = getConfigurator(version);
    configurator.configure(testng, options);
    postConfigure(testng, testSourceDirectory, classifier, reportManager, suite, reportsDirectory);
    testng.setTestClasses(testClasses);
    testng.run();
  }
  
  public static void run(List suiteFiles, String testSourceDirectory, Map options, ArtifactVersion version, String classifier, ReporterManager reportManager, SurefireTestSuite suite, File reportsDirectory)
    throws TestSetFailedException
  {
    TestNG testng = new TestNG(true);
    Configurator configurator = getConfigurator(version);
    configurator.configure(testng, options);
    postConfigure(testng, testSourceDirectory, classifier, reportManager, suite, reportsDirectory);
    testng.setTestSuites(suiteFiles);
    testng.run();
  }
  
  private static Configurator getConfigurator(ArtifactVersion version)
    throws TestSetFailedException
  {
    try
    {
      VersionRange range = VersionRange.createFromVersionSpec("[4.7,5.1]");
      if (range.containsVersion(version)) {
        return new TestNG4751Configurator();
      }
      range = VersionRange.createFromVersionSpec("[5.2]");
      if (range.containsVersion(version)) {
        return new TestNG52Configurator();
      }
      range = VersionRange.createFromVersionSpec("[5.3,)");
      if (range.containsVersion(version)) {
        return new TestNGMapConfigurator();
      }
      throw new TestSetFailedException("Unknown TestNG version " + version);
    }
    catch (InvalidVersionSpecificationException invsex)
    {
      throw new TestSetFailedException("Bug in plugin. Please report it with the attached stacktrace", invsex);
    }
  }
  
  private static void postConfigure(TestNG testNG, String sourcePath, String classifier, ReporterManager reportManager, SurefireTestSuite suite, File reportsDirectory)
    throws TestSetFailedException
  {
    testNG.setVerbose(0);
    
    TestNGReporter reporter = createTestNGReporter(reportManager, suite);
    testNG.addListener(reporter);
    if (sourcePath != null) {
      testNG.setSourcePath(sourcePath);
    }
    testNG.setOutputDirectory(reportsDirectory.getAbsolutePath());
  }
  
  private static TestNGReporter createTestNGReporter(ReporterManager reportManager, SurefireTestSuite suite)
  {
    try
    {
      Class.forName("org.testng.internal.IResultListener");
      Class c = Class.forName("org.apache.maven.surefire.testng.ConfigurationAwareTestNGReporter");
      try
      {
        Constructor ctor = c.getConstructor(new Class[] { ReporterManager.class, SurefireTestSuite.class });
        return (TestNGReporter)ctor.newInstance(new Object[] { reportManager, suite });
      }
      catch (Exception e)
      {
        throw new RuntimeException("Bug in ConfigurationAwareTestNGReporter", e);
      }
      return new TestNGReporter(reportManager);
    }
    catch (ClassNotFoundException e) {}
  }
  
  private static void attachNonStandardReporter(TestNG testNG, String className)
  {
    try
    {
      Class c = Class.forName(className);
      if (IReporter.class.isAssignableFrom(c)) {
        testNG.addListener(c.newInstance());
      }
    }
    catch (Exception e) {}
  }
}
           

TestNG類關鍵代碼:

public void run() {
    initializeSuitesAndJarFile();
    initializeConfiguration();
    initializeDefaultListeners();
    initializeCommandLineSuites();
    initializeCommandLineSuitesParams();
    initializeCommandLineSuitesGroups();

    sanityCheck();

    List<ISuite> suiteRunners = null;

    runExecutionListeners(true /* start */);

    m_start = System.currentTimeMillis();

    //
    // Slave mode
    //
    if (m_slavefileName != null) {
       SuiteSlave slave = new SuiteSlave( m_slavefileName, this );
       slave.waitForSuites();
    }

    //
    // Regular mode
    //
    else if (m_masterfileName == null) {
      suiteRunners = runSuitesLocally();
    }

    //
    // Master mode
    //
    else {
       SuiteDispatcher dispatcher = new SuiteDispatcher(m_masterfileName);
       suiteRunners = dispatcher.dispatch(getConfiguration(),
           m_suites, getOutputDirectory(),
           getTestListeners());
    }

    m_end = System.currentTimeMillis();
    runExecutionListeners(false /* finish */);

    if(null != suiteRunners) {
      generateReports(suiteRunners);
    }

    if(!m_hasTests) {
      setStatus(HAS_NO_TEST);
      if (TestRunner.getVerbose() > 1) {
        System.err.println("[TestNG] No tests found. Nothing was run");
        usage();
      }
    }
  }
           
public List<ISuite> runSuitesLocally() {
    SuiteRunnerMap suiteRunnerMap = new SuiteRunnerMap();
    if (m_suites.size() > 0) {
      if (m_suites.get(0).getVerbose() >= 2) {
        Version.displayBanner();
      }

      // First initialize the suite runners to ensure there are no configuration issues.
      // Create a map with XmlSuite as key and corresponding SuiteRunner as value
      for (XmlSuite xmlSuite : m_suites) {
        createSuiteRunners(suiteRunnerMap, xmlSuite);
      }

      //
      // Run suites
      //
      if (m_suiteThreadPoolSize == 1 && !m_randomizeSuites) {
        // Single threaded and not randomized: run the suites in order
        for (XmlSuite xmlSuite : m_suites) {
          runSuitesSequentially(xmlSuite, suiteRunnerMap, getVerbose(xmlSuite),
              getDefaultSuiteName());
        }
      } else {
        // Multithreaded: generate a dynamic graph that stores the suite hierarchy. This is then
        // used to run related suites in specific order. Parent suites are run only
        // once all the child suites have completed execution
        DynamicGraph<ISuite> suiteGraph = new DynamicGraph<ISuite>();
        for (XmlSuite xmlSuite : m_suites) {
          populateSuiteGraph(suiteGraph, suiteRunnerMap, xmlSuite);
        }

        IThreadWorkerFactory<ISuite> factory = new SuiteWorkerFactory(suiteRunnerMap,
          0 /* verbose hasn't been set yet */, getDefaultSuiteName());
        GraphThreadPoolExecutor<ISuite> pooledExecutor =
          new GraphThreadPoolExecutor<ISuite>(suiteGraph, factory, m_suiteThreadPoolSize,
          m_suiteThreadPoolSize, Integer.MAX_VALUE, TimeUnit.MILLISECONDS,
          new LinkedBlockingQueue<Runnable>());

        Utils.log("TestNG", 2, "Starting executor for all suites");
        // Run all suites in parallel
        pooledExecutor.run();
        try {
          pooledExecutor.awaitTermination(Long.MAX_VALUE, TimeUnit.MILLISECONDS);
          pooledExecutor.shutdownNow();
        }
        catch (InterruptedException handled) {
          Thread.currentThread().interrupt();
          error("Error waiting for concurrent executors to finish " + handled.getMessage());
        }
      }
    }
    else {
      setStatus(HAS_NO_TEST);
      error("No test suite found. Nothing to run");
      usage();
    }