You are viewing a plain text version of this content. The canonical link for it is here.
Posted to surefire-commits@maven.apache.org by kr...@apache.org on 2010/05/20 11:55:34 UTC

svn commit: r946586 - in /maven/surefire/trunk/surefire-api/src/main/java/org/apache/maven/surefire: ./ report/

Author: krosenvold
Date: Thu May 20 09:55:34 2010
New Revision: 946586

URL: http://svn.apache.org/viewvc?rev=946586&view=rev
Log:
o Refactored ReporterManager to extract some responsibilities into separate classes.

No functional change

ReporterManager had too many responsibilities within the same class, making it
hard to analyze this class for further changes.

Newly added Javadoc within ReporterManager explains  design weaknesses within
the current ReporterManager that severely limit concurrency potential of all
parallel surefire providers.

Added:
    maven/surefire/trunk/surefire-api/src/main/java/org/apache/maven/surefire/report/MulticastingReporter.java   (with props)
    maven/surefire/trunk/surefire-api/src/main/java/org/apache/maven/surefire/report/RunStatistics.java   (with props)
    maven/surefire/trunk/surefire-api/src/main/java/org/apache/maven/surefire/report/SystemStreamCapturer.java   (with props)
    maven/surefire/trunk/surefire-api/src/main/java/org/apache/maven/surefire/report/TestSetStatistics.java   (with props)
Modified:
    maven/surefire/trunk/surefire-api/src/main/java/org/apache/maven/surefire/Surefire.java
    maven/surefire/trunk/surefire-api/src/main/java/org/apache/maven/surefire/report/AbstractReporter.java
    maven/surefire/trunk/surefire-api/src/main/java/org/apache/maven/surefire/report/Reporter.java
    maven/surefire/trunk/surefire-api/src/main/java/org/apache/maven/surefire/report/ReporterManager.java

Modified: maven/surefire/trunk/surefire-api/src/main/java/org/apache/maven/surefire/Surefire.java
URL: http://svn.apache.org/viewvc/maven/surefire/trunk/surefire-api/src/main/java/org/apache/maven/surefire/Surefire.java?rev=946586&r1=946585&r2=946586&view=diff
==============================================================================
--- maven/surefire/trunk/surefire-api/src/main/java/org/apache/maven/surefire/Surefire.java (original)
+++ maven/surefire/trunk/surefire-api/src/main/java/org/apache/maven/surefire/Surefire.java Thu May 20 09:55:34 2010
@@ -22,6 +22,7 @@ package org.apache.maven.surefire;
 import org.apache.maven.surefire.report.Reporter;
 import org.apache.maven.surefire.report.ReporterException;
 import org.apache.maven.surefire.report.ReporterManager;
+import org.apache.maven.surefire.report.RunStatistics;
 import org.apache.maven.surefire.suite.SurefireTestSuite;
 import org.apache.maven.surefire.testset.TestSetFailedException;
 
@@ -79,18 +80,18 @@ public class Surefire
                     Boolean failIfNoTests )
         throws ReporterException, TestSetFailedException
     {
+        RunStatistics runStatistics = new RunStatistics();
         ReporterManager reporterManager =
-            new ReporterManager( instantiateReports( reportDefinitions, surefireClassLoader ) );
+            new ReporterManager( instantiateReports( reportDefinitions, surefireClassLoader ) , runStatistics);
 
         if ( results != null )
         {
-            reporterManager.initResultsFromProperties( results );
+            runStatistics.initResultsFromProperties( results );
         }
 
         int totalTests = 0;
 
-        SurefireTestSuite suite =
-            createSuiteFromDefinition( testSuiteDefinition, surefireClassLoader, testsClassLoader );
+        SurefireTestSuite suite = createSuiteFromDefinition( testSuiteDefinition, surefireClassLoader, testsClassLoader );
 
         int testCount = suite.getNumTests();
         if ( testCount > 0 )
@@ -113,25 +114,19 @@ public class Surefire
 
         if ( results != null )
         {
-            reporterManager.updateResultsProperties( results );
+            runStatistics.updateResultsProperties( results );
         }
-        
+
         if ( failIfNoTests.booleanValue() )
         {
-            if ( reporterManager.getNbTests() == 0 )
+            if ( runStatistics.getCompletedCount() == 0 )
             {
                 return NO_TESTS;
             }
         }
         
-        if ( reporterManager.getNumErrors() == 0 && reporterManager.getNumFailures() == 0 )
-        {
-            return SUCCESS;
-        }
-        else
-        {
-            return FAILURE;
-        }
+
+        return runStatistics.isProblemFree() ? SUCCESS : FAILURE;
 
     }
 
@@ -146,8 +141,9 @@ public class Surefire
                         ClassLoader testsClassLoader, Boolean failIfNoTests )
         throws ReporterException, TestSetFailedException
     {
+        RunStatistics runStatistics = new RunStatistics();
         ReporterManager reporterManager =
-            new ReporterManager( instantiateReports( reportDefinitions, surefireClassLoader ) );
+            new ReporterManager( instantiateReports( reportDefinitions, surefireClassLoader ), runStatistics );
 
         List suites = new ArrayList();
 
@@ -184,20 +180,13 @@ public class Surefire
         reporterManager.runCompleted();
         if ( failIfNoTests.booleanValue() )
         {
-            if ( reporterManager.getNbTests() == 0 )
+            if ( runStatistics.getCompletedCount() == 0 )
             {
                 return NO_TESTS;
             }
         }
 
-        if ( reporterManager.getNumErrors() == 0 && reporterManager.getNumFailures() == 0 )
-        {
-            return SUCCESS;
-        }
-        else
-        {
-            return FAILURE;
-        }
+        return runStatistics.isProblemFree() ? SUCCESS : FAILURE;
     }
 
     private SurefireTestSuite createSuiteFromDefinition( Object[] definition, ClassLoader surefireClassLoader,

Modified: maven/surefire/trunk/surefire-api/src/main/java/org/apache/maven/surefire/report/AbstractReporter.java
URL: http://svn.apache.org/viewvc/maven/surefire/trunk/surefire-api/src/main/java/org/apache/maven/surefire/report/AbstractReporter.java?rev=946586&r1=946585&r2=946586&view=diff
==============================================================================
--- maven/surefire/trunk/surefire-api/src/main/java/org/apache/maven/surefire/report/AbstractReporter.java (original)
+++ maven/surefire/trunk/surefire-api/src/main/java/org/apache/maven/surefire/report/AbstractReporter.java Thu May 20 09:55:34 2010
@@ -20,8 +20,6 @@ package org.apache.maven.surefire.report
  */
 
 import java.text.NumberFormat;
-import java.util.ArrayList;
-import java.util.Collection;
 import java.util.Locale;
 
 /**
@@ -35,18 +33,8 @@ public abstract class AbstractReporter
 
     protected int errors;
 
-    /**
-     * Holds the source(s) that causes the error(s).
-     */
-    private Collection errorSources = new ArrayList();
-
     protected int failures;
 
-    /**
-     * Holds the source(s) that causes the failure(s).
-     */
-    private Collection failureSources = new ArrayList();
-
     protected long startTime;
 
     protected long endTime;
@@ -86,14 +74,6 @@ public abstract class AbstractReporter
     {
     }
 
-    public void runStopped()
-    {
-    }
-
-    public void runAborted( ReportEntry report )
-    {
-    }
-
     public void testSetStarting( ReportEntry report )
         throws ReporterException
     {
@@ -105,26 +85,6 @@ public abstract class AbstractReporter
     {
     }
 
-    public void testSetAborted( ReportEntry report )
-    {
-    }
-
-    /**
-     * @see org.apache.maven.surefire.report.Reporter#getFailureSources()
-     */
-    public Collection getFailureSources()
-    {
-        return this.failureSources;
-    }
-
-    /**
-     * @see org.apache.maven.surefire.report.Reporter#getErrorSources()
-     */
-    public Collection getErrorSources()
-    {
-        return this.errorSources;
-    }
-
     // ----------------------------------------------------------------------
     // Test
     // ----------------------------------------------------------------------
@@ -149,14 +109,12 @@ public abstract class AbstractReporter
     public void testError( ReportEntry report, String stdOut, String stdErr )
     {
         ++errors;
-        errorSources.add( report.getName() );
         endTest();
     }
 
     public void testFailed( ReportEntry report, String stdOut, String stdErr )
     {
         ++failures;
-        failureSources.add( report.getName() );
         endTest();
     }
 
@@ -211,10 +169,6 @@ public abstract class AbstractReporter
 
         completedCount = 0;
 
-        this.failureSources = new ArrayList();
-
-        this.errorSources = new ArrayList();
-
     }
 
     // ----------------------------------------------------------------------

Added: maven/surefire/trunk/surefire-api/src/main/java/org/apache/maven/surefire/report/MulticastingReporter.java
URL: http://svn.apache.org/viewvc/maven/surefire/trunk/surefire-api/src/main/java/org/apache/maven/surefire/report/MulticastingReporter.java?rev=946586&view=auto
==============================================================================
--- maven/surefire/trunk/surefire-api/src/main/java/org/apache/maven/surefire/report/MulticastingReporter.java (added)
+++ maven/surefire/trunk/surefire-api/src/main/java/org/apache/maven/surefire/report/MulticastingReporter.java Thu May 20 09:55:34 2010
@@ -0,0 +1,147 @@
+package org.apache.maven.surefire.report;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * A reporter that broadcasts to other reporters
+ *
+ * @author Kristian Rosenvold
+ */
+public class MulticastingReporter
+    implements Reporter
+{
+    private final List target;
+
+    public MulticastingReporter( List target )
+    {
+        this.target = target;
+    }
+
+    public void testSetStarting( ReportEntry report )
+        throws ReporterException
+    {
+        for ( Iterator it = target.iterator(); it.hasNext(); )
+        {
+            ( (Reporter) it.next() ).testSetStarting( report );
+        }
+    }
+
+    public void testSetCompleted( ReportEntry report )
+    {
+        for ( Iterator it = target.iterator(); it.hasNext(); )
+        {
+            try
+            {
+                ( (Reporter) it.next() ).testSetCompleted( report );
+            }
+            catch ( ReporterException e )
+            {
+                // Added in commit r331325 in ReporterManager. This smells fishy. What's this about ?
+            }
+
+        }
+    }
+
+
+    public void runStarting( int testCount )
+    {
+        for ( Iterator it = target.iterator(); it.hasNext(); )
+        {
+            ( (Reporter) it.next() ).runStarting( testCount );
+        }
+    }
+
+    public void runCompleted()
+    {
+        for ( Iterator it = target.iterator(); it.hasNext(); )
+        {
+            ( (Reporter) it.next() ).runCompleted();
+        }
+    }
+
+
+    public void testStarting( ReportEntry report )
+    {
+        for ( Iterator it = target.iterator(); it.hasNext(); )
+        {
+            ( (Reporter) it.next() ).testStarting( report );
+        }
+    }
+
+    public void testSucceeded( ReportEntry report )
+    {
+        for ( Iterator it = target.iterator(); it.hasNext(); )
+        {
+            ( (Reporter) it.next() ).testSucceeded( report );
+        }
+    }
+
+    public void testError( ReportEntry report, String stdOut, String stdErr )
+    {
+        for ( Iterator it = target.iterator(); it.hasNext(); )
+        {
+            ( (Reporter) it.next() ).testError( report, stdOut, stdErr );
+        }
+    }
+
+    public void testFailed( ReportEntry report, String stdOut, String stdErr )
+    {
+        for ( Iterator it = target.iterator(); it.hasNext(); )
+        {
+            ( (Reporter) it.next() ).testFailed( report, stdOut, stdErr );
+        }
+    }
+
+    public void testSkipped( ReportEntry report )
+    {
+        for ( Iterator it = target.iterator(); it.hasNext(); )
+        {
+            ( (Reporter) it.next() ).testSkipped( report );
+        }
+    }
+
+
+    public void writeMessage( String message )
+    {
+        for ( Iterator it = target.iterator(); it.hasNext(); )
+        {
+            ( (Reporter) it.next() ).writeMessage( message );
+        }
+    }
+
+    public void writeFooter( String footer )
+    {
+        for ( Iterator it = target.iterator(); it.hasNext(); )
+        {
+            ( (Reporter) it.next() ).writeFooter( footer );
+        }
+    }
+
+    public void reset()
+    {
+        for ( Iterator it = target.iterator(); it.hasNext(); )
+        {
+            ( (Reporter) it.next() ).reset();
+        }
+    }
+}

Propchange: maven/surefire/trunk/surefire-api/src/main/java/org/apache/maven/surefire/report/MulticastingReporter.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: maven/surefire/trunk/surefire-api/src/main/java/org/apache/maven/surefire/report/Reporter.java
URL: http://svn.apache.org/viewvc/maven/surefire/trunk/surefire-api/src/main/java/org/apache/maven/surefire/report/Reporter.java?rev=946586&r1=946585&r2=946586&view=diff
==============================================================================
--- maven/surefire/trunk/surefire-api/src/main/java/org/apache/maven/surefire/report/Reporter.java (original)
+++ maven/surefire/trunk/surefire-api/src/main/java/org/apache/maven/surefire/report/Reporter.java Thu May 20 09:55:34 2010
@@ -19,8 +19,6 @@ package org.apache.maven.surefire.report
  * under the License.
  */
 
-import java.util.Collection;
-
 /**
  * Contract between the different implementations of the Surefire reporters
  *
@@ -28,19 +26,11 @@ import java.util.Collection;
  */
 public interface Reporter
 {
-    void writeMessage( String message );
-
-    void writeFooter( String footer );
-
     // The entire run
     void runStarting( int testCount );
 
     void runCompleted();
 
-    void runStopped();
-
-    void runAborted( ReportEntry report );
-
     // Test Sets
     void testSetStarting( ReportEntry report )
         throws ReporterException;
@@ -48,8 +38,6 @@ public interface Reporter
     void testSetCompleted( ReportEntry report )
         throws ReporterException;
 
-    void testSetAborted( ReportEntry report );
-
     // Tests
 
     /**
@@ -89,45 +77,8 @@ public interface Reporter
     // Counters
     void reset();
 
-    /**
-     * Get the number of errors
-     *
-     * @return
-     */
-    int getNumErrors();
-
-    /**
-     * Get the number of failures
-     *
-     * @return
-     */
-    int getNumFailures();
-
-    /**
-     * Get the number of tests
-     *
-     * @return
-     */
-    int getNumTests();
-
-    /**
-     * Get the number of tests skipped
-     *
-     * @return
-     */
-    int getNumSkipped();
+    void writeMessage( String message );
 
-    /**
-     * Gives the source(s) that causes the error(s).
-     *
-     * @return The source(s).
-     */
-    Collection getErrorSources();
+    void writeFooter( String footer );
 
-    /**
-     * Gives the source(s) that causes the failures(s).
-     *
-     * @return The source(s).
-     */
-    Collection getFailureSources();
 }

Modified: maven/surefire/trunk/surefire-api/src/main/java/org/apache/maven/surefire/report/ReporterManager.java
URL: http://svn.apache.org/viewvc/maven/surefire/trunk/surefire-api/src/main/java/org/apache/maven/surefire/report/ReporterManager.java?rev=946586&r1=946585&r2=946586&view=diff
==============================================================================
--- maven/surefire/trunk/surefire-api/src/main/java/org/apache/maven/surefire/report/ReporterManager.java (original)
+++ maven/surefire/trunk/surefire-api/src/main/java/org/apache/maven/surefire/report/ReporterManager.java Thu May 20 09:55:34 2010
@@ -19,99 +19,54 @@ package org.apache.maven.surefire.report
  * under the License.
  */
 
-import org.apache.maven.surefire.util.TeeStream;
-import org.codehaus.plexus.util.IOUtil;
-
-import java.io.ByteArrayOutputStream;
-import java.io.PrintStream;
-import java.util.ArrayList;
-import java.util.Collection;
 import java.util.Iterator;
 import java.util.List;
-import java.util.Properties;
 
+/**
+ * A reporting front-end for providers.
+ * <p/>
+ * Synchronization/Threading note:
+ * <p/>
+ * This design is really only good for single-threaded test execution. Although it is currently
+ * used by multi-threaded providers too, the design does not really make sense (and is probably buggy).
+ * <p/>
+ * This is because to get correct results, the client basically needs to do something like this:
+ * synchronized( ReporterManger.getClass()){
+ * reporterManager.runStarted()
+ * reporterManager.testSetStarting()
+ * reporterManager.testStarting()
+ * reporterManager.testSucceeded()
+ * reporterManager.testSetCompleted()
+ * reporterManager.runCompleted()
+ * }
+ * <p/>
+ * This is because the underlying providers are singletons and keep state, if you remove the outer synchronized
+ * block, you may get mixups between results from different tests; although the end result (total test count etc)
+ * should probably be correct.
+ * <p/>
+ * The solution to this problem involves making a clearer separation between test-result collection and reporting,
+ * preferably removing singleton state approach out of the reporting interface.
+ * <p/>
+ * Please also note that the synchronization requirements of this interface severely limit the concurrency
+ * potential of all the parallel surefire providers, especially when runnning non-io bound tests,
+ */
 public class ReporterManager
 {
-    private int completedCount;
-
-    private int errors;
-
-    /**
-     * Holds the sources of the error.
-     */
-    private Collection errorSources = new ArrayList();
-
-    private int failures;
-
-    /**
-     * Holds the sources of the failures.
-     */
-    private Collection failureSources = new ArrayList();
-
-    private List reports;
-
-    private PrintStream oldOut;
-
-    private PrintStream oldErr;
-
-    private PrintStream newErr;
+    private final RunStatistics runStatistics;
 
-    private PrintStream newOut;
+    private final MulticastingReporter multicastingReporter;
 
-    private int skipped;
+    private final SystemStreamCapturer consoleCapturer = new SystemStreamCapturer();
 
-    private static final String RESULTS_ERRORS = "errors";
-
-    private static final String RESULTS_COMPLETED_COUNT = "completedCount";
-
-    private static final String RESULTS_FAILURES = "failures";
-
-    private static final String RESULTS_SKIPPED = "skipped";
-
-    public ReporterManager( List reports )
+    public ReporterManager( List reports, RunStatistics runStatistics )
     {
-        this.reports = reports;
-    }
-
-    public void addReporter( Reporter reporter )
-    {
-        if ( reporter == null )
-        {
-            throw new NullPointerException();
-        }
-
-        if ( !reports.contains( reporter ) )
-        {
-            reports.add( reporter );
-        }
-    }
-
-    public void removeReport( Reporter report )
-    {
-        if ( report == null )
-        {
-            throw new NullPointerException();
-        }
-
-        if ( reports.contains( report ) )
-        {
-            reports.remove( report );
-        }
-    }
-
-    public List getReports()
-    {
-        return reports;
+        multicastingReporter = new MulticastingReporter( reports );
+        this.runStatistics = runStatistics;
     }
 
     public synchronized void writeMessage( String message )
     {
-        for ( Iterator i = reports.iterator(); i.hasNext(); )
-        {
-            Reporter report = (Reporter) i.next();
-
-            report.writeMessage( message );
-        }
+        multicastingReporter.writeMessage( message );
     }
 
     // ----------------------------------------------------------------------
@@ -125,142 +80,52 @@ public class ReporterManager
             throw new IllegalArgumentException( "testCount is less than zero" );
         }
 
-        for ( Iterator i = reports.iterator(); i.hasNext(); )
-        {
-            Reporter report = (Reporter) i.next();
-
-            report.runStarting( testCount );
-        }
-    }
-
-    public synchronized void runStopped()
-    {
-        for ( Iterator it = reports.iterator(); it.hasNext(); )
-        {
-            Reporter reporter = (Reporter) it.next();
-
-            reporter.runStopped();
-        }
-    }
-
-    public synchronized void runAborted( ReportEntry report )
-    {
-        if ( report == null )
-        {
-            throw new NullPointerException();
-        }
-
-        for ( Iterator it = reports.iterator(); it.hasNext(); )
-        {
-            Reporter reporter = (Reporter) it.next();
-
-            reporter.runAborted( report );
-        }
-
-        ++errors;
+        multicastingReporter.runStarting( testCount );
     }
 
     public synchronized void runCompleted()
     {
-        for ( Iterator it = reports.iterator(); it.hasNext(); )
+        multicastingReporter.runCompleted();
+        multicastingReporter.writeFooter( "" );
+        multicastingReporter.writeFooter( "Results :" );
+        multicastingReporter.writeFooter( "" );
+        if ( runStatistics.hadFailures() )
         {
-            Reporter reporter = (Reporter) it.next();
-
-            reporter.runCompleted();
-        }
-
-        writeFooter( "" );
-        writeFooter( "Results :" );
-        writeFooter( "" );
-        if ( failures > 0 )
-        {
-            writeFooter( "Failed tests: " );
-            for ( Iterator iterator = this.failureSources.iterator(); iterator.hasNext(); )
+            multicastingReporter.writeFooter( "Failed tests: " );
+            for ( Iterator iterator = this.runStatistics.getFailureSources().iterator(); iterator.hasNext(); )
             {
-                writeFooter( "  " + iterator.next() );
+                multicastingReporter.writeFooter( "  " + iterator.next() );
             }
-            writeFooter( "" );
+            multicastingReporter.writeFooter( "" );
         }
-        if ( errors > 0 )
+        if ( runStatistics.hadErrors() )
         {
             writeFooter( "Tests in error: " );
-            for ( Iterator iterator = this.errorSources.iterator(); iterator.hasNext(); )
+            for ( Iterator iterator = this.runStatistics.getErrorSources().iterator(); iterator.hasNext(); )
             {
-                writeFooter( "  " + iterator.next() );
+                multicastingReporter.writeFooter( "  " + iterator.next() );
             }
-            writeFooter( "" );
+            multicastingReporter.writeFooter( "" );
         }
-        writeFooter( "Tests run: " + completedCount + ", Failures: " + failures + ", Errors: " + errors
-            + ", Skipped: " + skipped );
-        writeFooter( "" );
+        multicastingReporter.writeFooter( runStatistics.getSummary() );
+        multicastingReporter.writeFooter( "" );
     }
 
     private synchronized void writeFooter( String footer )
     {
-        for ( Iterator i = reports.iterator(); i.hasNext(); )
-        {
-            Reporter report = (Reporter) i.next();
-
-            report.writeFooter( footer );
-        }
+        multicastingReporter.writeFooter( footer );
     }
 
-    private ByteArrayOutputStream stdOut;
-
-    private ByteArrayOutputStream stdErr;
 
     public synchronized void testSetStarting( ReportEntry report )
         throws ReporterException
     {
-        for ( Iterator it = reports.iterator(); it.hasNext(); )
-        {
-            Reporter reporter = (Reporter) it.next();
-
-            reporter.testSetStarting( report );
-        }
+        multicastingReporter.testSetStarting( report );
     }
 
     public synchronized void testSetCompleted( ReportEntry report )
     {
-        if ( !reports.isEmpty() )
-        {
-            Reporter reporter = (Reporter) reports.get( 0 );
-
-            skipped += reporter.getNumSkipped();
-
-            errors += reporter.getNumErrors();
-            errorSources.addAll( reporter.getErrorSources() );
-
-            failures += reporter.getNumFailures();
-            failureSources.addAll( reporter.getFailureSources() );
-
-            completedCount += reporter.getNumTests();
-        }
-
-        for ( Iterator it = reports.iterator(); it.hasNext(); )
-        {
-            Reporter reporter = (Reporter) it.next();
-
-            try
-            {
-                reporter.testSetCompleted( report );
-            }
-            catch ( Exception e )
-            {
-            }
-        }
-    }
-
-    public synchronized void testSetAborted( ReportEntry report )
-    {
-        for ( Iterator it = reports.iterator(); it.hasNext(); )
-        {
-            Reporter reporter = (Reporter) it.next();
-
-            reporter.testSetAborted( report );
-        }
-
-        ++errors;
+        multicastingReporter.testSetCompleted( report );
     }
 
     // ----------------------------------------------------------------------
@@ -269,148 +134,48 @@ public class ReporterManager
 
     public synchronized void testStarting( ReportEntry report )
     {
-        stdOut = new ByteArrayOutputStream();
-
-        newOut = new PrintStream( stdOut );
-
-        oldOut = System.out;
-
-        TeeStream tee = new TeeStream( oldOut, newOut );
-        System.setOut( tee );
-
-        stdErr = new ByteArrayOutputStream();
-
-        newErr = new PrintStream( stdErr );
-
-        oldErr = System.err;
-
-        tee = new TeeStream( oldErr, newErr );
-        System.setErr( tee );
-
-        for ( Iterator it = reports.iterator(); it.hasNext(); )
-        {
-            Reporter reporter = (Reporter) it.next();
-
-            reporter.testStarting( report );
-        }
+        consoleCapturer.startCapture();
+        multicastingReporter.testStarting( report );
     }
 
     public synchronized void testSucceeded( ReportEntry report )
     {
-        resetStreams();
-
-        for ( Iterator it = reports.iterator(); it.hasNext(); )
-        {
-            Reporter reporter = (Reporter) it.next();
-
-            reporter.testSucceeded( report );
-        }
+        consoleCapturer.resetStreams();
+        runStatistics.incrementCompletedCount();
+        multicastingReporter.testSucceeded( report );
     }
 
     public synchronized void testError( ReportEntry reportEntry )
     {
-        testFailed( reportEntry, "error" );
+        multicastingReporter.testError( reportEntry, consoleCapturer.getStdOutLog(), consoleCapturer.getStdErrLog() );
+        runStatistics.incrementErrorsCount();
+        runStatistics.addErrorSource( reportEntry.getName() );
+        consoleCapturer.resetStreams();
     }
 
     public synchronized void testFailed( ReportEntry reportEntry )
     {
-        testFailed( reportEntry, "failure" );
-    }
-
-    private synchronized void testFailed( ReportEntry reportEntry, String typeError )
-    {
-        // Note that the fields can be null if the test hasn't even started yet (an early error)
-        String stdOutLog = stdOut != null ? stdOut.toString() : "";
-
-        String stdErrLog = stdErr != null ? stdErr.toString() : "";
-
-        resetStreams();
-
-        for ( Iterator it = reports.iterator(); it.hasNext(); )
-        {
-            Reporter reporter = (Reporter) it.next();
-
-            if ( "failure".equals( typeError ) )
-            {
-                reporter.testFailed( reportEntry, stdOutLog, stdErrLog );
-            }
-            else
-            {
-                reporter.testError( reportEntry, stdOutLog, stdErrLog );
-            }
-        }
-    }
-
-    private void resetStreams()
-    {
-        // Note that the fields can be null if the test hasn't even started yet (an early error)
-        if ( oldOut != null )
-        {
-            System.setOut( oldOut );
-        }
-        if ( oldErr != null )
-        {
-            System.setErr( oldErr );
-        }
-
-        IOUtil.close( newOut );
-        IOUtil.close( newErr );
-    }
-
-    public synchronized void reset()
-    {
-        for ( Iterator it = reports.iterator(); it.hasNext(); )
-        {
-            Reporter report = (Reporter) it.next();
-
-            report.reset();
-        }
+        multicastingReporter.testFailed( reportEntry, consoleCapturer.getStdOutLog(), consoleCapturer.getStdErrLog() );
+        runStatistics.incrementFailureCount();
+        runStatistics.addFailureSource( reportEntry.getName() );
+        consoleCapturer.resetStreams();
     }
 
     // ----------------------------------------------------------------------
     // Counters
     // ----------------------------------------------------------------------
 
-    public int getNumErrors()
-    {
-        return errors;
-    }
-
-    public int getNumFailures()
-    {
-        return failures;
-    }
-
-    public int getNbTests()
-    {
-        return completedCount;
-    }
-
     public synchronized void testSkipped( ReportEntry report )
     {
-        resetStreams();
-
-        for ( Iterator it = reports.iterator(); it.hasNext(); )
-        {
-            Reporter reporter = (Reporter) it.next();
+        consoleCapturer.resetStreams();
 
-            reporter.testSkipped( report );
-        }
+        runStatistics.incrementSkippedCount();
+        multicastingReporter.testSkipped( report );
     }
 
-    public void initResultsFromProperties( Properties results )
+    public synchronized void reset()
     {
-        errors = Integer.valueOf( results.getProperty( RESULTS_ERRORS, "0" ) ).intValue();
-        skipped = Integer.valueOf( results.getProperty( RESULTS_SKIPPED, "0" ) ).intValue();
-        failures = Integer.valueOf( results.getProperty( RESULTS_FAILURES, "0" ) ).intValue();
-        completedCount = Integer.valueOf( results.getProperty( RESULTS_COMPLETED_COUNT, "0" ) ).intValue();
+        multicastingReporter.reset();
     }
 
-    public void updateResultsProperties( Properties results )
-    {
-        results.setProperty( RESULTS_ERRORS, String.valueOf( errors ) );
-        results.setProperty( RESULTS_COMPLETED_COUNT, String.valueOf( completedCount ) );
-        results.setProperty( RESULTS_FAILURES, String.valueOf( failures ) );
-        results.setProperty( RESULTS_SKIPPED, String.valueOf( skipped ) );
-    }
 }

Added: maven/surefire/trunk/surefire-api/src/main/java/org/apache/maven/surefire/report/RunStatistics.java
URL: http://svn.apache.org/viewvc/maven/surefire/trunk/surefire-api/src/main/java/org/apache/maven/surefire/report/RunStatistics.java?rev=946586&view=auto
==============================================================================
--- maven/surefire/trunk/surefire-api/src/main/java/org/apache/maven/surefire/report/RunStatistics.java (added)
+++ maven/surefire/trunk/surefire-api/src/main/java/org/apache/maven/surefire/report/RunStatistics.java Thu May 20 09:55:34 2010
@@ -0,0 +1,78 @@
+package org.apache.maven.surefire.report;
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+
+/**
+ * @author Kristian Rosenvold
+ */
+public class RunStatistics
+    extends TestSetStatistics
+{
+
+
+    /**
+     * Holds the source(s) that causes the error(s).
+     */
+    private final Collection errorSources = new ArrayList();
+
+    /**
+     * Holds the source(s) that causes the failure(s).
+     */
+    private final Collection failureSources = new ArrayList();
+
+
+    public void addErrorSource( String errorSource )
+    {
+        synchronized ( errorSources )
+        {
+            errorSources.add( errorSource );
+        }
+    }
+
+    public void addFailureSource( String errorSource )
+    {
+        synchronized ( failureSources )
+        {
+            failureSources.add( errorSource );
+        }
+    }
+
+
+    public Collection getFailureSources()
+    {
+        synchronized ( failureSources )
+        {
+            return Collections.unmodifiableCollection( failureSources );
+        }
+    }
+
+    public Collection getErrorSources()
+    {
+        synchronized ( errorSources )
+        {
+            return Collections.unmodifiableCollection( errorSources );
+        }
+    }
+
+
+}

Propchange: maven/surefire/trunk/surefire-api/src/main/java/org/apache/maven/surefire/report/RunStatistics.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: maven/surefire/trunk/surefire-api/src/main/java/org/apache/maven/surefire/report/SystemStreamCapturer.java
URL: http://svn.apache.org/viewvc/maven/surefire/trunk/surefire-api/src/main/java/org/apache/maven/surefire/report/SystemStreamCapturer.java?rev=946586&view=auto
==============================================================================
--- maven/surefire/trunk/surefire-api/src/main/java/org/apache/maven/surefire/report/SystemStreamCapturer.java (added)
+++ maven/surefire/trunk/surefire-api/src/main/java/org/apache/maven/surefire/report/SystemStreamCapturer.java Thu May 20 09:55:34 2010
@@ -0,0 +1,102 @@
+package org.apache.maven.surefire.report;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.surefire.util.TeeStream;
+import org.codehaus.plexus.util.IOUtil;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+
+/**
+ * Captures System.out/System.err streams to buffers.
+ *
+ * Please note that this design is inherently single-threaded test-linear, and is intended only
+ * for use with ReporterManager, which is also test-linear. While it will capture
+ * output in a multi-threaded scenario, there's no way to associate ouput with the correct
+ * test/thread.
+ *
+ * Note; this class does not need synchronization because all of these methods are serially invoked on
+ * the same thread. Or maybe not. See notes inside ReporterManager about the general improperness
+ * of this design in multithreading.
+ */
+public class SystemStreamCapturer
+{
+    private PrintStream oldOut;
+
+    private PrintStream oldErr;
+
+    private PrintStream newErr;
+
+    private PrintStream newOut;
+
+    private ByteArrayOutputStream stdOut;
+
+    private ByteArrayOutputStream stdErr;
+
+    public void startCapture()
+    {
+        stdOut = new ByteArrayOutputStream();
+
+        newOut = new PrintStream( stdOut );
+
+        oldOut = System.out;
+
+        TeeStream tee = new TeeStream( oldOut, newOut );
+        System.setOut( tee );
+
+        stdErr = new ByteArrayOutputStream();
+
+        newErr = new PrintStream( stdErr );
+
+        oldErr = System.err;
+
+        tee = new TeeStream( oldErr, newErr );
+        System.setErr( tee );
+    }
+
+    public void resetStreams()
+    {
+        // Note that the fields can be null if the test hasn't even started yet (an early error)
+        if ( oldOut != null )
+        {
+            System.setOut( oldOut );
+        }
+        if ( oldErr != null )
+        {
+            System.setErr( oldErr );
+        }
+
+        IOUtil.close( newOut );
+        IOUtil.close( newErr );
+    }
+
+    public String getStdOutLog()
+    {
+        // Note that the fields can be null if the test hasn't even started yet (an early error)
+        return stdOut != null ? stdOut.toString() : "";
+    }
+
+    public String getStdErrLog()
+    {
+        // Note that the fields can be null if the test hasn't even started yet (an early error)
+        return stdErr != null ? stdErr.toString() : "";
+    }
+}

Propchange: maven/surefire/trunk/surefire-api/src/main/java/org/apache/maven/surefire/report/SystemStreamCapturer.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: maven/surefire/trunk/surefire-api/src/main/java/org/apache/maven/surefire/report/TestSetStatistics.java
URL: http://svn.apache.org/viewvc/maven/surefire/trunk/surefire-api/src/main/java/org/apache/maven/surefire/report/TestSetStatistics.java?rev=946586&view=auto
==============================================================================
--- maven/surefire/trunk/surefire-api/src/main/java/org/apache/maven/surefire/report/TestSetStatistics.java (added)
+++ maven/surefire/trunk/surefire-api/src/main/java/org/apache/maven/surefire/report/TestSetStatistics.java Thu May 20 09:55:34 2010
@@ -0,0 +1,112 @@
+package org.apache.maven.surefire.report;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.util.Properties;
+
+/**
+ * Run-statistcis for a testset
+ *
+ * @author Kristian Rosenvold
+ *         Note; synchronization is questionable. Whiled this class alone is ok, there's a higher level concern about
+ *         synchronization interactions within ReporterManager. See ReporterManager class.
+ */
+public class TestSetStatistics
+{
+    private static final String RESULTS_ERRORS = "errors";
+
+    private static final String RESULTS_COMPLETED_COUNT = "completedCount";
+
+    private static final String RESULTS_FAILURES = "failures";
+
+    private static final String RESULTS_SKIPPED = "skipped";
+
+
+    protected int completedCount;
+
+    protected int errors;
+
+    protected int failures;
+
+    protected int skipped;
+
+    public synchronized void incrementCompletedCount()
+    {
+        completedCount += 1;
+    }
+
+    public synchronized void incrementErrorsCount()
+    {
+        errors += 1;
+    }
+
+    public synchronized void incrementFailureCount()
+    {
+        failures += 1;
+    }
+
+    public synchronized void incrementSkippedCount()
+    {
+        skipped += 1;
+    }
+
+    public synchronized boolean isProblemFree()
+    {
+        return !hadFailures() && !hadErrors();
+    }
+
+    public synchronized boolean hadFailures()
+    {
+        return failures > 0;
+    }
+
+    public synchronized boolean hadErrors()
+    {
+        return errors > 0;
+    }
+
+    public synchronized int getCompletedCount()
+    {
+        return completedCount;
+    }
+
+    public synchronized void initResultsFromProperties( Properties results )
+    {
+        errors = Integer.valueOf( results.getProperty( RESULTS_ERRORS, "0" ) ).intValue();
+        skipped = Integer.valueOf( results.getProperty( RESULTS_SKIPPED, "0" ) ).intValue();
+        failures = Integer.valueOf( results.getProperty( RESULTS_FAILURES, "0" ) ).intValue();
+        completedCount = Integer.valueOf( results.getProperty( RESULTS_COMPLETED_COUNT, "0" ) ).intValue();
+    }
+
+    public synchronized void updateResultsProperties( Properties results )
+    {
+        results.setProperty( RESULTS_ERRORS, String.valueOf( errors ) );
+        results.setProperty( RESULTS_COMPLETED_COUNT, String.valueOf( completedCount ) );
+        results.setProperty( RESULTS_FAILURES, String.valueOf( failures ) );
+        results.setProperty( RESULTS_SKIPPED, String.valueOf( skipped ) );
+    }
+
+    public synchronized String getSummary()
+    {
+        return "Tests run: " + completedCount + ", Failures: " + failures + ", Errors: " + errors + ", Skipped: " +
+            skipped;
+    }
+
+}

Propchange: maven/surefire/trunk/surefire-api/src/main/java/org/apache/maven/surefire/report/TestSetStatistics.java
------------------------------------------------------------------------------
    svn:eol-style = native