Implemented -skipfailedinvocationcounts (also in ant and testng.xml).

diff --git a/src/main/org/testng/SuiteRunner.java b/src/main/org/testng/SuiteRunner.java
index db9d78d..d09fe8c 100644
--- a/src/main/org/testng/SuiteRunner.java
+++ b/src/main/org/testng/SuiteRunner.java
@@ -58,6 +58,7 @@
   transient private IAnnotationFinder m_jdkAnnotationFinder;
   
   transient private IObjectFactory m_objectFactory;
+  transient private Boolean m_skipFailedInvocationCounts = Boolean.FALSE;
   
 //  transient private IAnnotationTransformer m_annotationTransformer = null;
 
@@ -167,7 +168,7 @@
     if (null == m_tmpRunnerFactory) {
       factory = new DefaultTestRunnerFactory(
           m_testlisteners.toArray(new ITestListener[m_testlisteners.size()]), 
-          m_useDefaultListeners);
+          m_useDefaultListeners, m_skipFailedInvocationCounts);
     }
     else {
       factory = new ProxyTestRunnerFactory(
@@ -431,21 +432,31 @@
   public static class DefaultTestRunnerFactory implements ITestRunnerFactory {
     private ITestListener[] m_failureGenerators;
     private boolean m_useDefaultListeners;
+    private boolean m_skipFailedInvocationCounts;
     
-    public DefaultTestRunnerFactory(ITestListener[] failureListeners, boolean useDefaultListeners) {
+    public DefaultTestRunnerFactory(ITestListener[] failureListeners,
+        boolean useDefaultListeners,
+        boolean skipFailedInvocationCounts)
+    {
       m_failureGenerators = failureListeners;
       m_useDefaultListeners = useDefaultListeners;
+      m_skipFailedInvocationCounts = skipFailedInvocationCounts;
     }
 
     /**
      * @see ITestRunnerFactory#newTestRunner(org.testng.ISuite, org.testng.xml.XmlTest)
      */
     public TestRunner newTestRunner(ISuite suite, XmlTest test) {
+      boolean skip = m_skipFailedInvocationCounts;
+      if (! skip) {
+        skip = test.skipFailedInvocationCounts();
+      }
       TestRunner testRunner = 
         new TestRunner(suite,
                         test,
                         suite.getOutputDirectory(),
-                        suite.getAnnotationFinder(test.getAnnotations()));
+                        suite.getAnnotationFinder(test.getAnnotations()),
+                        skip);
       
       if (m_useDefaultListeners) {
         testRunner.addListener(new TestHTMLReporter());
@@ -462,7 +473,7 @@
       for (ITestListener itl : m_failureGenerators) {
         testRunner.addListener(itl);
       }
-
+      
       return testRunner;
     }
   }
@@ -501,11 +512,17 @@
   }
 
   private SuiteRunState m_suiteState= new SuiteRunState();
-  
+
   /**
    * @see org.testng.ISuite#getSuiteState()
    */
   public SuiteRunState getSuiteState() {
     return m_suiteState;
   }
+
+  public void setSkipFailedInvocationCounts(Boolean skipFailedInvocationCounts) {
+    if (skipFailedInvocationCounts != null) {
+      m_skipFailedInvocationCounts = skipFailedInvocationCounts;
+    }
+  }
 }
diff --git a/src/main/org/testng/TestNG.java b/src/main/org/testng/TestNG.java
index 2361ca7..127f32a 100644
--- a/src/main/org/testng/TestNG.java
+++ b/src/main/org/testng/TestNG.java
@@ -602,6 +602,8 @@
 
   private IAnnotationTransformer m_annotationTransformer = new DefaultAnnotationTransformer();
 
+  private Boolean m_skipFailedInvocationCounts = false;
+
   /**
    * Sets the level of verbosity. This value will override the value specified 
    * in the test suites.
@@ -756,6 +758,15 @@
           xmlSuite.setJUnit(m_isJUnit);
         }
         
+        // If the skip flag was invoked on the command line, it
+        // takes precedence
+        if (null != m_skipFailedInvocationCounts) {
+          xmlSuite.setSkipFailedInvocationCounts(m_skipFailedInvocationCounts);
+        }
+        else {
+          m_skipFailedInvocationCounts = xmlSuite.skipFailedInvocationCounts();
+        }
+        
         // TODO CQ is this OK? Should the command line verbose flag override 
         // what is explicitly specified in the suite?
         if (null != m_verbose) {
@@ -781,7 +792,12 @@
         m_outputDir, 
         m_testRunnerFactory, 
         m_useDefaultListeners, 
-        new IAnnotationFinder[] {m_javadocAnnotationFinder, m_jdkAnnotationFinder}, m_objectFactory);
+        new IAnnotationFinder[] {
+          m_javadocAnnotationFinder, 
+          m_jdkAnnotationFinder
+        },
+        m_objectFactory);
+    result.setSkipFailedInvocationCounts(m_skipFailedInvocationCounts);
 
     for (ISuiteListener isl : m_suiteListeners) {
       result.addListener(isl);
@@ -877,6 +893,9 @@
     setJUnit((Boolean) cmdLineArgs.get(TestNGCommandLineArgs.JUNIT_DEF_OPT));
     setMaster( (String)cmdLineArgs.get(TestNGCommandLineArgs.MASTER_OPT));
     setSlave( (String)cmdLineArgs.get(TestNGCommandLineArgs.SLAVE_OPT));
+    setSkipFailedInvocationCounts(
+      (Boolean) cmdLineArgs.get(
+        TestNGCommandLineArgs.SKIP_FAILED_INVOCATION_COUNT_OPT));
     
     String parallelMode = (String) cmdLineArgs.get(TestNGCommandLineArgs.PARALLEL_MODE);
     if (parallelMode != null) {
@@ -917,6 +936,10 @@
     }
   }
 
+  private void setSkipFailedInvocationCounts(Boolean skip) {
+    m_skipFailedInvocationCounts = skip;
+  }
+
   private void addReporter(ReporterConfig reporterConfig) {
     Object instance = reporterConfig.newReporterInstance();
     if (instance != null) {
@@ -1060,6 +1083,14 @@
     return m_annotationTransformer;
   }
   
+  public boolean getSkipFailedInvocationCounts() {
+    return m_skipFailedInvocationCounts;
+  }
+  
+  public void setSkipFailedInvocationCounts(boolean skip) {
+    m_skipFailedInvocationCounts = skip;
+  }
+  
   public void setAnnotationTransformer(IAnnotationTransformer t) {
     m_annotationTransformer = t;
   }
diff --git a/src/main/org/testng/TestNGAntTask.java b/src/main/org/testng/TestNGAntTask.java
index fa8a755..06a48ee 100644
--- a/src/main/org/testng/TestNGAntTask.java
+++ b/src/main/org/testng/TestNGAntTask.java
@@ -134,6 +134,7 @@
   public String m_useDefaultListeners;

   private String m_suiteName="Ant suite";

   private String m_testName="Ant test";

+  private Boolean m_skipFailedInvocationCounts;

 

   /**

    * The list of report listeners added via <reporter> sub-element of the Ant task

@@ -464,11 +465,18 @@
 

     List<String> argv= new ArrayList<String>();

 

-    if(null != m_isJUnit) {

+    if (null != m_isJUnit) {

       if(m_isJUnit.booleanValue()) {

         argv.add(TestNGCommandLineArgs.JUNIT_DEF_OPT);

       }

     }

+    

+    if (null != m_skipFailedInvocationCounts) {

+      if(m_skipFailedInvocationCounts.booleanValue()) {

+        argv.add(TestNGCommandLineArgs.SKIP_FAILED_INVOCATION_COUNT_OPT);

+      }

+    }

+    

 

     if(null != m_verbose) {

       argv.add(TestNGCommandLineArgs.LOG);

@@ -1011,4 +1019,8 @@
   public void addConfiguredReporter(ReporterConfig reporterConfig) {

     reporterConfigs.add(reporterConfig);

   }

+  

+  public void setSkipFailedInvocationCounts(boolean skip) {

+    m_skipFailedInvocationCounts = Boolean.valueOf(skip);

+  }

 }

diff --git a/src/main/org/testng/TestNGCommandLineArgs.java b/src/main/org/testng/TestNGCommandLineArgs.java
index 2e28493..16c78f1 100644
--- a/src/main/org/testng/TestNGCommandLineArgs.java
+++ b/src/main/org/testng/TestNGCommandLineArgs.java
@@ -69,6 +69,8 @@
   public static final String PARALLEL_MODE = "-parallel";
   public static final String SUITE_NAME_OPT = "-suitename";
   public static final String TEST_NAME_OPT = "-testname";
+  public static final String SKIP_FAILED_INVOCATION_COUNT_OPT = "-skipfailedinvocationcounts";
+
   /**
    * Used to pass a reporter configuration in the form <code>-reporter <reporter_name_or_class>:option=value[,option=value]</code>
    */
@@ -354,6 +356,9 @@
           i++;
         }
       }
+      else if (SKIP_FAILED_INVOCATION_COUNT_OPT.equalsIgnoreCase(argv[i])) {
+        arguments.put(SKIP_FAILED_INVOCATION_COUNT_OPT, Boolean.TRUE);
+      }
       //
       // Unknown option
       //
diff --git a/src/main/org/testng/TestRunner.java b/src/main/org/testng/TestRunner.java
index 2bb8627..0afe5c3 100644
--- a/src/main/org/testng/TestRunner.java
+++ b/src/main/org/testng/TestRunner.java
@@ -62,6 +62,7 @@
   transient private List<IConfigurationListener> m_configurationListeners= new ArrayList<IConfigurationListener>();
 
   transient private IConfigurationListener m_confListener= new ConfigurationListener();
+  transient private boolean m_skipFailedInvocationCounts;
   
   /**
    * All the test methods we found, associated with their respective classes.
@@ -120,31 +121,38 @@
   public TestRunner(ISuite suite,
                     XmlTest test,
                     String outputDirectory,
-                    IAnnotationFinder finder) 
+                    IAnnotationFinder finder,
+                    boolean skipFailedInvocationCounts) 
   {
-    init(suite, test, outputDirectory, finder);
+    init(suite, test, outputDirectory, finder, skipFailedInvocationCounts);
   }
 
-  public TestRunner(ISuite suite, XmlTest test, IAnnotationFinder finder) 
+  public TestRunner(ISuite suite, XmlTest test, 
+    IAnnotationFinder finder, boolean skipFailedInvocationCounts) 
   {
-    init(suite, test, suite.getOutputDirectory(), finder);
+    init(suite, test, suite.getOutputDirectory(), finder, skipFailedInvocationCounts);
   }
 
-  public TestRunner(ISuite suite, XmlTest test) {
+  public TestRunner(ISuite suite, XmlTest test, 
+    boolean skipFailedInvocationCounts)
+  {
     init(suite, test, suite.getOutputDirectory(), 
-        suite.getAnnotationFinder(test.getAnnotations()));
+        suite.getAnnotationFinder(test.getAnnotations()),
+        skipFailedInvocationCounts);
   }
 
   private void init(ISuite suite,
                     XmlTest test,
                     String outputDirectory,
-                    IAnnotationFinder annotationFinder)
+                    IAnnotationFinder annotationFinder,
+                    boolean skipFailedInvocationCounts)
   {
     m_xmlTest= test;
     m_suite = suite;
     m_testName = test.getName();
     m_host = suite.getHost();
     m_testClassesFromXml= test.getXmlClasses();
+    m_skipFailedInvocationCounts = skipFailedInvocationCounts;
     
     m_packageNamesFromXml= test.getXmlPackages();    
     if(null != m_packageNamesFromXml) {
@@ -154,7 +162,9 @@
     }
 
     m_annotationFinder= annotationFinder;
-    m_invoker= new Invoker(this, this, m_suite.getSuiteState(), m_annotationFinder);
+    m_invoker = 
+      new Invoker(this, this, m_suite.getSuiteState(), 
+        m_annotationFinder, m_skipFailedInvocationCounts);
 
     setVerbose(test.getVerbose());
 
diff --git a/src/main/org/testng/internal/IInvoker.java b/src/main/org/testng/internal/IInvoker.java
index cd5d344..e35fdaa 100644
--- a/src/main/org/testng/internal/IInvoker.java
+++ b/src/main/org/testng/internal/IInvoker.java
@@ -52,4 +52,5 @@
                                              ConfigurationGroupMethods groupMethods,
                                              Object[] instances,
                                              ITestContext testContext);
+
 }
diff --git a/src/main/org/testng/internal/Invoker.java b/src/main/org/testng/internal/Invoker.java
index d661cea..9ce8f36 100644
--- a/src/main/org/testng/internal/Invoker.java
+++ b/src/main/org/testng/internal/Invoker.java
@@ -49,15 +49,18 @@
   private ITestResultNotifier m_notifier;
   private IAnnotationFinder m_annotationFinder;
   private SuiteRunState m_suiteState;
+  private boolean m_skipFailedInvocationCounts;
 
   public Invoker(ITestContext testContext,
                  ITestResultNotifier notifier,
                  SuiteRunState state,
-                 IAnnotationFinder annotationFinder) {
+                 IAnnotationFinder annotationFinder,
+                 boolean skipFailedInvocationCounts) {
     m_testContext= testContext;
     m_suiteState= state;
     m_notifier= notifier;
     m_annotationFinder= annotationFinder;
+    m_skipFailedInvocationCounts = skipFailedInvocationCounts;
   }
 
   /**
@@ -764,7 +767,7 @@
     // - try to remove the isWithinThreadedMethod: still needed to determine the @BeforeMethod + @AfterMethod
     // - [DONE] solve the results different approaches: assignment and addAll
     //
-    // HINT: for invocationCount>1 and threadPoolSize>1 the method will be invoked on a thread pool
+    // For invocationCount>1 and threadPoolSize>1 the method will be invoked on a thread pool
     int invocationCount = (testMethod.getThreadPoolSize() > 1 ? 1 : testMethod.getInvocationCount());
     
     int failureCount = 0;
@@ -780,7 +783,7 @@
         //
         if (MethodHelper.isEnabled(testMethod.getMethod(), m_annotationFinder)) {
             //
-            // HINT: If threadPoolSize specified, run this method in its own pool thread.
+            // If threadPoolSize specified, run this method in its own pool thread.
             //
             if (testMethod.getThreadPoolSize() > 1 && testMethod.getInvocationCount() > 1) {
                 return invokePooledTestMethods(testMethod, allTestMethods, suite, 
@@ -849,14 +852,36 @@
                       for (int i = 0; i < failedInstances.size(); i++) {
                         List<ITestResult> retryResults = new ArrayList<ITestResult>();
 
-                        failureCount = retryFailed(failedInstances.toArray(),
-                                                   i, testMethod, suite, testClass, beforeMethods,
-                                                   afterMethods, groupMethods, retryResults,
-                                                   failureCount, expectedExceptionClasses,
-                                                   testContext, parameters, parametersIndex);
-                        result.addAll(retryResults);
+                        failureCount = 
+                         retryFailed(failedInstances.toArray(),
+                         i, testMethod, suite, testClass, beforeMethods,
+                         afterMethods, groupMethods, retryResults,
+                         failureCount, expectedExceptionClasses,
+                         testContext, parameters, parametersIndex);
+                      result.addAll(retryResults);
                       }
                     }
+                    
+                    //
+                    // If we have a failure, skip all the
+                    // other invocationCounts
+                    //
+                    if (failureCount > 0 && m_skipFailedInvocationCounts) {
+                      while (invocationCount-- > 0) {
+                        ITestResult r = 
+                          new TestResult(testMethod.getTestClass(),
+                            instances[0],
+                            testMethod,
+                            null,
+                            start,
+                            System.currentTimeMillis());
+                        r.setStatus(TestResult.SKIP);
+                        result.add(r);
+                        runTestListeners(r);
+                        m_notifier.addSkippedTest(testMethod, r);
+                      }
+                      break;
+                    }
                   }
                   parametersIndex++;
                 }
diff --git a/src/main/org/testng/remote/RemoteTestNG.java b/src/main/org/testng/remote/RemoteTestNG.java
index c147b80..d14cd11 100644
--- a/src/main/org/testng/remote/RemoteTestNG.java
+++ b/src/main/org/testng/remote/RemoteTestNG.java
@@ -99,8 +99,10 @@
     if(null == m_customTestRunnerFactory) {

       m_customTestRunnerFactory= new ITestRunnerFactory() {

           public TestRunner newTestRunner(ISuite suite, XmlTest xmlTest) {

-            TestRunner runner= new TestRunner(suite, xmlTest);

-            if(m_useDefaultListeners) {

+            TestRunner runner =

+              new TestRunner(suite, xmlTest,

+              false /*skipFailedInvocationCounts */);

+            if (m_useDefaultListeners) {

               runner.addListener(new TestHTMLReporter());

               runner.addListener(new JUnitXMLReporter());

             }

diff --git a/src/main/org/testng/remote/SuiteDispatcher.java b/src/main/org/testng/remote/SuiteDispatcher.java
index 76442d4..b875836 100644
--- a/src/main/org/testng/remote/SuiteDispatcher.java
+++ b/src/main/org/testng/remote/SuiteDispatcher.java
@@ -114,6 +114,7 @@
 						tmpSuite.setXmlPackages(suite.getXmlPackages());

 						tmpSuite.setAnnotations(suite.getAnnotations());

 						tmpSuite.setJUnit(suite.isJUnit());

+           tmpSuite.setSkipFailedInvocationCounts(suite.skipFailedInvocationCounts());

 						tmpSuite.setName("Temporary suite for " + test.getName());

 						tmpSuite.setParallel(suite.getParallel());

 						tmpSuite.setParameters(suite.getParameters());

diff --git a/src/main/org/testng/xml/TestNGContentHandler.java b/src/main/org/testng/xml/TestNGContentHandler.java
index 9349718..6746734 100644
--- a/src/main/org/testng/xml/TestNGContentHandler.java
+++ b/src/main/org/testng/xml/TestNGContentHandler.java
@@ -134,6 +134,10 @@
           Utils.log("Parser", 1, "[WARN] Unknown value of attribute 'parallel' at suite level: '" + parallel + "'.");
         }
       }
+      String skip = attributes.getValue("skipfailedinvocationcounts");
+      if (skip != null) {
+        m_currentSuite.setSkipFailedInvocationCounts(Boolean.valueOf(skip));
+      }
       String threadCount = attributes.getValue("thread-count");
       if (null != threadCount) {
         m_currentSuite.setThreadCount(Integer.parseInt(threadCount));
@@ -222,6 +226,10 @@
       if (null != jUnit) {
         m_currentTest.setJUnit( Boolean.valueOf(jUnit).booleanValue());
       }
+      String skip = attributes.getValue("skipfailedinvocationcounts");
+      if (skip != null) {
+        m_currentTest.setSkipFailedInvocationCounts(Boolean.valueOf(skip).booleanValue());
+      }
       String parallel = attributes.getValue("parallel");
       if (null != parallel) {
         if(XmlSuite.PARALLEL_METHODS.equals(parallel)
diff --git a/src/main/org/testng/xml/XmlSuite.java b/src/main/org/testng/xml/XmlSuite.java
index 88ed23e..cd3959a 100644
--- a/src/main/org/testng/xml/XmlSuite.java
+++ b/src/main/org/testng/xml/XmlSuite.java
@@ -54,6 +54,8 @@
   /** JUnit compatibility flag. */
   private Boolean m_isJUnit = Boolean.FALSE;
   
+  private Boolean m_skipFailedInvocationCounts = Boolean.FALSE;
+  
   /** The thread count. */
   private int m_threadCount = 5;
   
@@ -326,7 +328,7 @@
   public Boolean isJUnit() {
     return m_isJUnit;
   }
-
+  
   /**
    * Sets the JUnit compatibility flag.
    *
@@ -336,6 +338,14 @@
     m_isJUnit = isJUnit;
   }
 
+  public Boolean skipFailedInvocationCounts() {
+    return m_skipFailedInvocationCounts;
+  }
+
+  public void setSkipFailedInvocationCounts(boolean skip) {
+    m_skipFailedInvocationCounts = skip;
+  }
+
   /**
    * Sets the XML packages.
    *
@@ -372,6 +382,9 @@
     p.setProperty("thread-count", String.valueOf(getThreadCount()));
     p.setProperty("annotations", getAnnotations());
     p.setProperty("junit", m_isJUnit != null ? m_isJUnit.toString() : "false"); // TESTNG-141
+    p.setProperty("skipfailedinvocationCounts", 
+        m_skipFailedInvocationCounts != null
+          ? m_skipFailedInvocationCounts.toString() : "false");
     if(null != m_objectFactory)
       p.setProperty("object-factory", m_objectFactory.getClass().getName());
     xsb.push("suite", p);
@@ -455,6 +468,7 @@
     result.setBeanShellExpression(getExpression());
     result.setMethodSelectors(getMethodSelectors());
     result.setJUnit(isJUnit()); // TESTNG-141
+    result.setSkipFailedInvocationCounts(skipFailedInvocationCounts());
     result.setObjectFactory(getObjectFactory());
     return result;
   }
diff --git a/src/main/org/testng/xml/XmlTest.java b/src/main/org/testng/xml/XmlTest.java
index 3a74ccf..bcd9ad9 100644
--- a/src/main/org/testng/xml/XmlTest.java
+++ b/src/main/org/testng/xml/XmlTest.java
@@ -43,6 +43,7 @@
   private List<XmlPackage> m_xmlPackages = new ArrayList<XmlPackage>();
   
   private String m_timeOut;
+  private Boolean m_skipFailedInvocationCounts;
 
   /**
    * Constructs a <code>XmlTest</code> and adds it to suite's list of tests. 
@@ -189,6 +190,22 @@
     m_isJUnit = isJUnit;
   }
   
+  public void setSkipFailedInvocationCounts(boolean skip) {
+    m_skipFailedInvocationCounts = skip;
+  }
+  
+  /**
+   * @return Returns the isJUnit.
+   */
+  public boolean skipFailedInvocationCounts() {
+    Boolean result = m_skipFailedInvocationCounts;
+    if (null == result) {
+      result = m_suite.skipFailedInvocationCounts();
+    }
+    
+    return result;
+  }
+  
   public void addMetaGroup(String name, List<String> metaGroup) {
     m_metaGroups.put(name, metaGroup);
   }