You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maven.apache.org by ap...@apache.org on 2007/05/14 03:34:18 UTC

svn commit: r537691 [7/11] - in /maven/sandbox/branches/surefire/surefire-collab2: ./ maven-surefire-plugin/ maven-surefire-plugin/src/ maven-surefire-plugin/src/it/ maven-surefire-plugin/src/it/test1/ maven-surefire-plugin/src/it/test1/src/ maven-sure...

Added: maven/sandbox/branches/surefire/surefire-collab2/surefire-api/src/main/java/org/apache/maven/surefire/report/ReporterManager.java
URL: http://svn.apache.org/viewvc/maven/sandbox/branches/surefire/surefire-collab2/surefire-api/src/main/java/org/apache/maven/surefire/report/ReporterManager.java?view=auto&rev=537691
==============================================================================
--- maven/sandbox/branches/surefire/surefire-collab2/surefire-api/src/main/java/org/apache/maven/surefire/report/ReporterManager.java (added)
+++ maven/sandbox/branches/surefire/surefire-collab2/surefire-api/src/main/java/org/apache/maven/surefire/report/ReporterManager.java Sun May 13 18:33:45 2007
@@ -0,0 +1,416 @@
+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;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Properties;
+
+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 PrintStream newOut;
+
+    private int skipped;
+
+    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 )
+    {
+        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;
+    }
+
+    public void writeMessage( String message )
+    {
+        for ( Iterator i = reports.iterator(); i.hasNext(); )
+        {
+            Reporter report = (Reporter) i.next();
+
+            report.writeMessage( message );
+        }
+    }
+
+    // ----------------------------------------------------------------------
+    // Run
+    // ----------------------------------------------------------------------
+
+    public void runStarting( int testCount )
+    {
+        if ( testCount < 0 )
+        {
+            throw new IllegalArgumentException( "testCount is less than zero" );
+        }
+
+        for ( Iterator i = reports.iterator(); i.hasNext(); )
+        {
+            Reporter report = (Reporter) i.next();
+
+            report.runStarting( testCount );
+        }
+    }
+
+    public void runStopped()
+    {
+        for ( Iterator it = reports.iterator(); it.hasNext(); )
+        {
+            Reporter reporter = (Reporter) it.next();
+
+            reporter.runStopped();
+        }
+    }
+
+    public 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;
+    }
+
+    public void runCompleted()
+    {
+        for ( Iterator it = reports.iterator(); it.hasNext(); )
+        {
+            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(); )
+            {
+                writeFooter( "  " + iterator.next() );
+            }
+            writeFooter( "" );
+        }
+        if ( errors > 0 )
+        {
+            writeFooter( "Tests in error: " );
+            for ( Iterator iterator = this.errorSources.iterator(); iterator.hasNext(); )
+            {
+                writeFooter( "  " + iterator.next() );
+            }
+            writeFooter( "" );
+        }
+        writeFooter( "Tests run: " + completedCount + ", Failures: " + failures + ", Errors: " + errors +
+            ", Skipped: " + skipped );
+        writeFooter( "" );
+    }
+
+    private void writeFooter( String footer )
+    {
+        for ( Iterator i = reports.iterator(); i.hasNext(); )
+        {
+            Reporter report = (Reporter) i.next();
+
+            report.writeFooter( footer );
+        }
+    }
+
+    private ByteArrayOutputStream stdOut;
+
+    private ByteArrayOutputStream stdErr;
+
+    public void testSetStarting( ReportEntry report )
+        throws ReporterException
+    {
+        for ( Iterator it = reports.iterator(); it.hasNext(); )
+        {
+            Reporter reporter = (Reporter) it.next();
+
+            reporter.testSetStarting( report );
+        }
+    }
+
+    public 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 void testSetAborted( ReportEntry report )
+    {
+        for ( Iterator it = reports.iterator(); it.hasNext(); )
+        {
+            Reporter reporter = (Reporter) it.next();
+
+            reporter.testSetAborted( report );
+        }
+
+        ++errors;
+    }
+
+    // ----------------------------------------------------------------------
+    // Test
+    // ----------------------------------------------------------------------
+
+    public 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 );
+        }
+    }
+
+    public void testSucceeded( ReportEntry report )
+    {
+        resetStreams();
+
+        for ( Iterator it = reports.iterator(); it.hasNext(); )
+        {
+            Reporter reporter = (Reporter) it.next();
+
+            reporter.testSucceeded( report );
+        }
+    }
+
+    public void testError( ReportEntry reportEntry )
+    {
+        testFailed( reportEntry, "error" );
+    }
+
+    public void testFailed( ReportEntry reportEntry )
+    {
+        testFailed( reportEntry, "failure" );
+    }
+
+    private 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 void reset()
+    {
+        for ( Iterator it = reports.iterator(); it.hasNext(); )
+        {
+            Reporter report = (Reporter) it.next();
+
+            report.reset();
+        }
+    }
+
+    // ----------------------------------------------------------------------
+    // Counters
+    // ----------------------------------------------------------------------
+
+    public int getNumErrors()
+    {
+        return errors;
+    }
+
+    public int getNumFailures()
+    {
+        return failures;
+    }
+
+    public int getNbTests()
+    {
+        return completedCount;
+    }
+
+    public void testSkipped( ReportEntry report )
+    {
+        resetStreams();
+
+        for ( Iterator it = reports.iterator(); it.hasNext(); )
+        {
+            Reporter reporter = (Reporter) it.next();
+
+            reporter.testSkipped( report );
+        }
+    }
+
+    public 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 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/sandbox/branches/surefire/surefire-collab2/surefire-api/src/main/java/org/apache/maven/surefire/report/StackTraceWriter.java
URL: http://svn.apache.org/viewvc/maven/sandbox/branches/surefire/surefire-collab2/surefire-api/src/main/java/org/apache/maven/surefire/report/StackTraceWriter.java?view=auto&rev=537691
==============================================================================
--- maven/sandbox/branches/surefire/surefire-collab2/surefire-api/src/main/java/org/apache/maven/surefire/report/StackTraceWriter.java (added)
+++ maven/sandbox/branches/surefire/surefire-collab2/surefire-api/src/main/java/org/apache/maven/surefire/report/StackTraceWriter.java Sun May 13 18:33:45 2007
@@ -0,0 +1,49 @@
+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.
+ */
+
+/**
+ * Ability to write a stack trace, filtered to omit locations inside Surefire and Maven.
+ *
+ * @author <a href="mailto:brett@apache.org">Brett Porter</a>
+ */
+public interface StackTraceWriter
+{
+    /**
+     * Write the throwable to a string, without trimming.
+     *
+     * @return the trace
+     */
+    String writeTraceToString();
+
+    /**
+     * Write the throwable to a string, trimming extra locations.
+     *
+     * @return the trace
+     */
+    String writeTrimmedTraceToString();
+
+    /**
+     * Retrieve the throwable for this writer.
+     *
+     * @return the throwable
+     */
+    Throwable getThrowable();
+}

Added: maven/sandbox/branches/surefire/surefire-collab2/surefire-api/src/main/java/org/apache/maven/surefire/report/XMLReporter.java
URL: http://svn.apache.org/viewvc/maven/sandbox/branches/surefire/surefire-collab2/surefire-api/src/main/java/org/apache/maven/surefire/report/XMLReporter.java?view=auto&rev=537691
==============================================================================
--- maven/sandbox/branches/surefire/surefire-collab2/surefire-api/src/main/java/org/apache/maven/surefire/report/XMLReporter.java (added)
+++ maven/sandbox/branches/surefire/surefire-collab2/surefire-api/src/main/java/org/apache/maven/surefire/report/XMLReporter.java Sun May 13 18:33:45 2007
@@ -0,0 +1,289 @@
+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.PrettyPrintXMLWriter;
+import org.codehaus.plexus.util.IOUtil;
+import org.codehaus.plexus.util.StringUtils;
+import org.codehaus.plexus.util.xml.Xpp3Dom;
+import org.codehaus.plexus.util.xml.Xpp3DomWriter;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Properties;
+import java.util.StringTokenizer;
+
+
+/**
+ * XML format reporter.
+ *
+ * @author <a href="mailto:jruiz@exist.com">Johnny R. Ruiz III</a>
+ * @version $Id: XMLReporter.java 510866 2007-02-23 08:13:49Z brett $
+ */
+public class XMLReporter
+    extends AbstractReporter
+{
+    private static final String LS = System.getProperty( "line.separator" );
+
+    private File reportsDirectory;
+
+    private List results = Collections.synchronizedList( new ArrayList() );
+
+    public XMLReporter( File reportsDirectory, Boolean trimStackTrace )
+    {
+        super( trimStackTrace );
+
+        this.reportsDirectory = reportsDirectory;
+    }
+
+    public void writeMessage( String message )
+    {
+    }
+
+    public void testSetCompleted( ReportEntry report )
+        throws ReporterException
+    {
+        super.testSetCompleted( report );
+
+        long runTime = System.currentTimeMillis() - testSetStartTime;
+
+        Xpp3Dom testSuite = createTestElement( "testsuite", report, runTime );
+
+        showProperties( testSuite );
+
+        testSuite.setAttribute( "tests", String.valueOf( this.getNumTests() ) );
+
+        testSuite.setAttribute( "errors", String.valueOf( this.getNumErrors() ) );
+
+        testSuite.setAttribute( "skipped", String.valueOf( this.getNumSkipped() ) );
+
+        testSuite.setAttribute( "failures", String.valueOf( this.getNumFailures() ) );
+
+        for ( Iterator i = results.iterator(); i.hasNext(); )
+        {
+            Xpp3Dom testcase = (Xpp3Dom) i.next();
+            testSuite.addChild( testcase );
+        }
+
+        File reportFile = new File( reportsDirectory, "TEST-" + report.getName() + ".xml" );
+
+        File reportDir = reportFile.getParentFile();
+
+        reportDir.mkdirs();
+
+        PrintWriter writer = null;
+
+        try
+        {
+            writer = new PrintWriter(
+                new BufferedWriter( new OutputStreamWriter( new FileOutputStream( reportFile ), "UTF-8" ) ) );
+
+            writer.write( "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>" + LS );
+
+            Xpp3DomWriter.write( new PrettyPrintXMLWriter( writer ), testSuite );
+        }
+        catch ( UnsupportedEncodingException e )
+        {
+            throw new ReporterException( "Unable to use UTF-8 encoding", e );
+        }
+        catch ( FileNotFoundException e )
+        {
+            throw new ReporterException( "Unable to create file: " + e.getMessage(), e );
+        }
+
+        finally
+        {
+            IOUtil.close( writer );
+        }
+    }
+
+    private String getReportName( ReportEntry report )
+    {
+        String reportName;
+
+        if ( report.getName().indexOf( "(" ) > 0 )
+        {
+            reportName = report.getName().substring( 0, report.getName().indexOf( "(" ) );
+        }
+        else
+        {
+            reportName = report.getName();
+        }
+        return reportName;
+    }
+
+    public void testSucceeded( ReportEntry report )
+    {
+        super.testSucceeded( report );
+
+        long runTime = this.endTime - this.startTime;
+
+        Xpp3Dom testCase = createTestElement( "testcase", report, runTime );
+
+        results.add( testCase );
+    }
+
+    private Xpp3Dom createTestElement( String name, ReportEntry report, long runTime )
+    {
+        Xpp3Dom testCase = new Xpp3Dom( name );
+        testCase.setAttribute( "name", getReportName( report ) );
+        if ( report.getGroup() != null )
+        {
+            testCase.setAttribute( "group", report.getGroup() );
+        }
+        testCase.setAttribute( "time", elapsedTimeAsString( runTime ) );
+        return testCase;
+    }
+
+    public void testError( ReportEntry report, String stdOut, String stdErr )
+    {
+        super.testError( report, stdOut, stdErr );
+
+        writeTestProblems( report, stdOut, stdErr, "error" );
+    }
+
+    public void testFailed( ReportEntry report, String stdOut, String stdErr )
+    {
+        super.testFailed( report, stdOut, stdErr );
+
+        writeTestProblems( report, stdOut, stdErr, "failure" );
+    }
+
+    private void writeTestProblems( ReportEntry report, String stdOut, String stdErr, String name )
+    {
+        long runTime = endTime - startTime;
+
+        Xpp3Dom testCase = createTestElement( "testcase", report, runTime );
+
+        Xpp3Dom element = createElement( testCase, name );
+
+        String stackTrace = getStackTrace( report );
+
+        Throwable t = report.getStackTraceWriter().getThrowable();
+
+        if ( t != null )
+        {
+
+            String message = t.getMessage();
+
+            if ( message != null && message.trim().length() > 0 )
+            {
+                element.setAttribute( "message", escapeAttribute( message ) );
+
+                element.setAttribute( "type", stackTrace.substring( 0, stackTrace.indexOf( ":" ) ) );
+            }
+            else
+            {
+                element.setAttribute( "type", new StringTokenizer( stackTrace ).nextToken() );
+            }
+        }
+
+        element.setValue( stackTrace );
+
+        addOutputStreamElement( stdOut, "system-out", testCase );
+
+        addOutputStreamElement( stdErr, "system-err", testCase );
+
+        results.add( testCase );
+    }
+
+    private void addOutputStreamElement( String stdOut, String name, Xpp3Dom testCase )
+    {
+        if ( stdOut != null && stdOut.trim().length() > 0 )
+        {
+            createElement( testCase, name ).setValue( stdOut );
+        }
+    }
+
+    private Xpp3Dom createElement( Xpp3Dom element, String name )
+    {
+        Xpp3Dom component = new Xpp3Dom( name );
+
+        element.addChild( component );
+
+        return component;
+    }
+
+    /**
+     * Adds system properties to the XML report.
+     *
+     * @param testSuite
+     */
+    private void showProperties( Xpp3Dom testSuite )
+    {
+        Xpp3Dom properties = createElement( testSuite, "properties" );
+
+        Properties systemProperties = System.getProperties();
+
+        if ( systemProperties != null )
+        {
+            Enumeration propertyKeys = systemProperties.propertyNames();
+
+            while ( propertyKeys.hasMoreElements() )
+            {
+                String key = (String) propertyKeys.nextElement();
+
+                String value = systemProperties.getProperty( key );
+
+                if ( value == null )
+                {
+                    value = "null";
+                }
+
+                Xpp3Dom property = createElement( properties, "property" );
+
+                property.setAttribute( "name", key );
+
+                property.setAttribute( "value", escapeAttribute( value ) );
+
+            }
+        }
+    }
+
+    private static String escapeAttribute( String attribute )
+    {
+        // Shouldn't Xpp3Dom do this itself?
+        String s = StringUtils.replace( attribute, "<", "&lt;" );
+        s = StringUtils.replace( s, ">", "&gt;" );
+        return s;
+
+    }
+
+    public Iterator getResults()
+    {
+        return results.iterator();
+    }
+
+    public void reset()
+    {
+        results.clear();
+        super.reset();
+    }
+}

Added: maven/sandbox/branches/surefire/surefire-collab2/surefire-api/src/main/java/org/apache/maven/surefire/suite/AbstractDirectoryTestSuite.java
URL: http://svn.apache.org/viewvc/maven/sandbox/branches/surefire/surefire-collab2/surefire-api/src/main/java/org/apache/maven/surefire/suite/AbstractDirectoryTestSuite.java?view=auto&rev=537691
==============================================================================
--- maven/sandbox/branches/surefire/surefire-collab2/surefire-api/src/main/java/org/apache/maven/surefire/suite/AbstractDirectoryTestSuite.java (added)
+++ maven/sandbox/branches/surefire/surefire-collab2/surefire-api/src/main/java/org/apache/maven/surefire/suite/AbstractDirectoryTestSuite.java Sun May 13 18:33:45 2007
@@ -0,0 +1,227 @@
+package org.apache.maven.surefire.suite;
+
+/*
+ * 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.Surefire;
+import org.apache.maven.surefire.report.ReportEntry;
+import org.apache.maven.surefire.report.ReporterException;
+import org.apache.maven.surefire.report.ReporterManager;
+import org.apache.maven.surefire.testset.SurefireTestSet;
+import org.apache.maven.surefire.testset.TestSetFailedException;
+import org.codehaus.plexus.util.DirectoryScanner;
+import org.codehaus.plexus.util.StringUtils;
+
+import java.io.File;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.ResourceBundle;
+
+public abstract class AbstractDirectoryTestSuite
+    implements SurefireTestSuite
+{
+    protected ResourceBundle bundle = ResourceBundle.getBundle( Surefire.SUREFIRE_BUNDLE_NAME );
+
+    private static final String FS = System.getProperty( "file.separator" );
+
+    private File basedir;
+
+    private List includes;
+
+    private List excludes;
+
+    protected Map testSets;
+
+    private int totalTests;
+
+    private static final String[] EMPTY_STRING_ARRAY = new String[0];
+
+    protected AbstractDirectoryTestSuite( File basedir, List includes, List excludes )
+    {
+        this.basedir = basedir;
+
+        this.includes = new ArrayList( includes );
+
+        this.excludes = new ArrayList( excludes );
+    }
+
+    public Map locateTestSets( ClassLoader classLoader )
+        throws TestSetFailedException
+    {
+        if ( testSets != null )
+        {
+            throw new IllegalStateException( "You can't call locateTestSets twice" );
+        }
+        testSets = new HashMap();
+
+        String[] tests = collectTests( basedir, includes, excludes );
+
+        for ( int i = 0; i < tests.length; i++ )
+        {
+            String className = tests[i];
+
+            Class testClass;
+            try
+            {
+                testClass = classLoader.loadClass( className );
+            }
+            catch ( ClassNotFoundException e )
+            {
+                throw new TestSetFailedException( "Unable to create test class '" + className + "'", e );
+            }
+
+            if ( !Modifier.isAbstract( testClass.getModifiers() ) )
+            {
+                SurefireTestSet testSet = createTestSet( testClass, classLoader );
+
+                if ( testSets.containsKey( testSet.getName() ) )
+                {
+                    throw new TestSetFailedException( "Duplicate test set '" + testSet.getName() + "'" );
+                }
+                testSets.put( testSet.getName(), testSet );
+
+                totalTests += testSet.getTestCount();
+            }
+        }
+
+        return Collections.unmodifiableMap( testSets );
+    }
+
+    protected abstract SurefireTestSet createTestSet( Class testClass, ClassLoader classLoader )
+        throws TestSetFailedException;
+
+    public void execute( ReporterManager reporterManager, ClassLoader classLoader )
+        throws ReporterException, TestSetFailedException
+    {
+        if ( testSets == null )
+        {
+            throw new IllegalStateException( "You must call locateTestSets before calling execute" );
+        }
+        for ( Iterator i = testSets.values().iterator(); i.hasNext(); )
+        {
+            SurefireTestSet testSet = (SurefireTestSet) i.next();
+
+            executeTestSet( testSet, reporterManager, classLoader );
+        }
+    }
+
+    private void executeTestSet( SurefireTestSet testSet, ReporterManager reporterManager, ClassLoader classLoader )
+        throws ReporterException, TestSetFailedException
+    {
+        String rawString = bundle.getString( "testSetStarting" );
+
+        ReportEntry report = new ReportEntry( this, testSet.getName(), rawString );
+
+        reporterManager.testSetStarting( report );
+
+        testSet.execute( reporterManager, classLoader );
+
+        rawString = bundle.getString( "testSetCompletedNormally" );
+
+        report = new ReportEntry( this, testSet.getName(), rawString );
+
+        reporterManager.testSetCompleted( report );
+
+        reporterManager.reset();
+    }
+
+    public void execute( String testSetName, ReporterManager reporterManager, ClassLoader classLoader )
+        throws ReporterException, TestSetFailedException
+    {
+        if ( testSets == null )
+        {
+            throw new IllegalStateException( "You must call locateTestSets before calling execute" );
+        }
+        SurefireTestSet testSet = (SurefireTestSet) testSets.get( testSetName );
+
+        if ( testSet == null )
+        {
+            throw new TestSetFailedException( "Unable to find test set '" + testSetName + "' in suite" );
+        }
+
+        executeTestSet( testSet, reporterManager, classLoader );
+    }
+
+    public int getNumTests()
+    {
+        if ( testSets == null )
+        {
+            throw new IllegalStateException( "You must call locateTestSets before calling getNumTestSets" );
+        }
+        return totalTests;
+    }
+
+    public int getNumTestSets()
+    {
+        if ( testSets == null )
+        {
+            throw new IllegalStateException( "You must call locateTestSets before calling getNumTestSets" );
+        }
+        return testSets.size();
+    }
+
+    private String[] collectTests( File basedir, List includes, List excludes )
+    {
+        String[] tests = EMPTY_STRING_ARRAY;
+        if ( basedir.exists() )
+        {
+            DirectoryScanner scanner = new DirectoryScanner();
+
+            scanner.setBasedir( basedir );
+
+            if ( includes != null )
+            {
+                scanner.setIncludes( processIncludesExcludes( includes ) );
+            }
+
+            if ( excludes != null )
+            {
+                scanner.setExcludes( processIncludesExcludes( excludes ) );
+            }
+
+            scanner.scan();
+
+            tests = scanner.getIncludedFiles();
+            for ( int i = 0; i < tests.length; i++ )
+            {
+                String test = tests[i];
+                test = test.substring( 0, test.indexOf( "." ) );
+                tests[i] = test.replace( FS.charAt( 0 ), '.' );
+            }
+        }
+        return tests;
+    }
+
+    private static String[] processIncludesExcludes( List list )
+    {
+        String[] incs = new String[list.size()];
+
+        for ( int i = 0; i < incs.length; i++ )
+        {
+            incs[i] = StringUtils.replace( (String) list.get( i ), "java", "class" );
+
+        }
+        return incs;
+    }
+}

Added: maven/sandbox/branches/surefire/surefire-collab2/surefire-api/src/main/java/org/apache/maven/surefire/suite/SurefireTestSuite.java
URL: http://svn.apache.org/viewvc/maven/sandbox/branches/surefire/surefire-collab2/surefire-api/src/main/java/org/apache/maven/surefire/suite/SurefireTestSuite.java?view=auto&rev=537691
==============================================================================
--- maven/sandbox/branches/surefire/surefire-collab2/surefire-api/src/main/java/org/apache/maven/surefire/suite/SurefireTestSuite.java (added)
+++ maven/sandbox/branches/surefire/surefire-collab2/surefire-api/src/main/java/org/apache/maven/surefire/suite/SurefireTestSuite.java Sun May 13 18:33:45 2007
@@ -0,0 +1,47 @@
+package org.apache.maven.surefire.suite;
+
+/*
+ * 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.report.ReporterException;
+import org.apache.maven.surefire.report.ReporterManager;
+import org.apache.maven.surefire.testset.TestSetFailedException;
+
+import java.util.Map;
+
+/**
+ * A complete test suite that contains one or more test sets.
+ *
+ * @author <a href="mailto:brett@apache.org">Brett Porter</a>
+ */
+public interface SurefireTestSuite
+{
+    void execute( ReporterManager reporterManager, ClassLoader classLoader )
+        throws ReporterException, TestSetFailedException;
+
+    void execute( String testSetName, ReporterManager reporterManager, ClassLoader classLoader )
+        throws ReporterException, TestSetFailedException;
+
+    int getNumTests();
+
+    int getNumTestSets();
+
+    Map locateTestSets( ClassLoader classLoader )
+        throws TestSetFailedException;
+}

Added: maven/sandbox/branches/surefire/surefire-collab2/surefire-api/src/main/java/org/apache/maven/surefire/testset/AbstractTestSet.java
URL: http://svn.apache.org/viewvc/maven/sandbox/branches/surefire/surefire-collab2/surefire-api/src/main/java/org/apache/maven/surefire/testset/AbstractTestSet.java?view=auto&rev=537691
==============================================================================
--- maven/sandbox/branches/surefire/surefire-collab2/surefire-api/src/main/java/org/apache/maven/surefire/testset/AbstractTestSet.java (added)
+++ maven/sandbox/branches/surefire/surefire-collab2/surefire-api/src/main/java/org/apache/maven/surefire/testset/AbstractTestSet.java Sun May 13 18:33:45 2007
@@ -0,0 +1,49 @@
+package org.apache.maven.surefire.testset;
+
+/*
+ * 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.
+ */
+
+/**
+ * Base test class.
+ */
+public abstract class AbstractTestSet
+    implements SurefireTestSet
+{
+    private Class testClass;
+
+    protected AbstractTestSet( Class testClass )
+    {
+        if ( testClass == null )
+        {
+            throw new NullPointerException( "testClass is null" );
+        }
+
+        this.testClass = testClass;
+    }
+
+    public String getName()
+    {
+        return testClass.getName();
+    }
+
+    public Class getTestClass()
+    {
+        return testClass;
+    }
+}

Added: maven/sandbox/branches/surefire/surefire-collab2/surefire-api/src/main/java/org/apache/maven/surefire/testset/PojoTestSet.java
URL: http://svn.apache.org/viewvc/maven/sandbox/branches/surefire/surefire-collab2/surefire-api/src/main/java/org/apache/maven/surefire/testset/PojoTestSet.java?view=auto&rev=537691
==============================================================================
--- maven/sandbox/branches/surefire/surefire-collab2/surefire-api/src/main/java/org/apache/maven/surefire/testset/PojoTestSet.java (added)
+++ maven/sandbox/branches/surefire/surefire-collab2/surefire-api/src/main/java/org/apache/maven/surefire/testset/PojoTestSet.java Sun May 13 18:33:45 2007
@@ -0,0 +1,299 @@
+package org.apache.maven.surefire.testset;
+
+/*
+ * 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.Surefire;
+import org.apache.maven.surefire.report.PojoStackTraceWriter;
+import org.apache.maven.surefire.report.ReportEntry;
+import org.apache.maven.surefire.report.ReporterManager;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.ResourceBundle;
+
+public class PojoTestSet
+    extends AbstractTestSet
+{
+    private ResourceBundle bundle = ResourceBundle.getBundle( Surefire.SUREFIRE_BUNDLE_NAME );
+
+    private static final String TEST_METHOD_PREFIX = "test";
+
+    private static final Object[] EMPTY_OBJECT_ARRAY = new Object[0];
+
+    private Object testObject;
+
+    protected List testMethods;
+
+    public PojoTestSet( Class testClass )
+        throws TestSetFailedException
+    {
+        super( testClass );
+
+        try
+        {
+            testObject = testClass.newInstance();
+        }
+        catch ( InstantiationException e )
+        {
+            throw new TestSetFailedException( "Unable to instantiate POJO '" + testClass + "'", e );
+        }
+        catch ( IllegalAccessException e )
+        {
+            throw new TestSetFailedException( "Unable to instantiate POJO '" + testClass + "'", e );
+        }
+    }
+
+    public void execute( ReporterManager reportManager, ClassLoader loader )
+        throws TestSetFailedException
+    {
+        if ( reportManager == null )
+        {
+            throw new NullPointerException( "reportManager is null" );
+        }
+
+        executeTestMethods( reportManager );
+    }
+
+    protected void executeTestMethods( ReporterManager reportManager )
+    {
+        if ( reportManager == null )
+        {
+            throw new NullPointerException( "reportManager is null" );
+        }
+
+        if ( testMethods == null )
+        {
+            discoverTestMethods();
+        }
+
+        boolean abort = false;
+
+        for ( int i = 0; i < testMethods.size() && !abort; ++i )
+        {
+            abort = executeTestMethod( (Method) testMethods.get( i ), EMPTY_OBJECT_ARRAY, reportManager );
+        }
+    }
+
+    /**
+     * @noinspection CatchGenericClass,OverlyBroadCatchBlock,MethodWithMultipleReturnPoints
+     */
+    protected boolean executeTestMethod( Method method, Object[] args, ReporterManager reportManager )
+    {
+        if ( method == null || args == null || reportManager == null )
+        {
+            throw new NullPointerException();
+        }
+
+        String userFriendlyMethodName = method.getName() + '(';
+
+        if ( args.length != 0 )
+        {
+            userFriendlyMethodName += "Reporter";
+        }
+
+        userFriendlyMethodName += ')';
+
+        ReportEntry report = new ReportEntry( testObject, getTestName( userFriendlyMethodName ), getName() );
+
+        reportManager.testStarting( report );
+
+        try
+        {
+            setUpFixture();
+        }
+        catch ( Exception e )
+        {
+            // Treat any exception from setUpFixture as a failure of the test.
+            String rawString = bundle.getString( "setupFixtureFailed" );
+
+            MessageFormat msgFmt = new MessageFormat( rawString );
+
+            Object[] stringArgs = {method.getName()};
+
+            String stringToPrint = msgFmt.format( stringArgs );
+
+            report = new ReportEntry( testObject, getTestName( userFriendlyMethodName ), stringToPrint,
+                                      new PojoStackTraceWriter( testObject.getClass().getName(), method.getName(),
+                                                                e ) );
+
+            reportManager.testFailed( report );
+
+            // A return value of true indicates to this class's executeTestMethods
+            // method that it should abort and not attempt to execute
+            // any other test methods. The other caller of this method,
+            // TestRerunner.rerun, ignores this return value, because it is
+            // only running one test.
+            return true;
+        }
+
+        // Make sure that tearDownFixture
+        try
+        {
+            method.invoke( testObject, args );
+
+            report = new ReportEntry( testObject, getTestName( userFriendlyMethodName ), getName() );
+
+            reportManager.testSucceeded( report );
+        }
+        catch ( InvocationTargetException ite )
+        {
+            Throwable t = ite.getTargetException();
+
+            String msg = t.getMessage();
+
+            if ( msg == null )
+            {
+                msg = t.toString();
+            }
+
+            report = new ReportEntry( testObject, getTestName( userFriendlyMethodName ), msg, new PojoStackTraceWriter(
+                testObject.getClass().getName(), method.getName(), t ) );
+
+            reportManager.testFailed( report );
+            // Don't return  here, because tearDownFixture should be called even
+            // if the test method throws an exception.
+        }
+        catch ( Throwable t )
+        {
+            String msg = t.getMessage();
+
+            if ( msg == null )
+            {
+                msg = t.toString();
+            }
+
+            report = new ReportEntry( testObject, getTestName( userFriendlyMethodName ), msg, new PojoStackTraceWriter(
+                testObject.getClass().getName(), method.getName(), t ) );
+
+            reportManager.testFailed( report );
+            // Don't return  here, because tearDownFixture should be called even
+            // if the test method throws an exception.
+        }
+
+        try
+        {
+            tearDownFixture();
+        }
+        catch ( Throwable t )
+        {
+            // Treat any exception from tearDownFixture as a failure of the test.
+            String rawString = bundle.getString( "cleanupFixtureFailed" );
+
+            MessageFormat msgFmt = new MessageFormat( rawString );
+
+            Object[] stringArgs = {method.getName()};
+
+            String stringToPrint = msgFmt.format( stringArgs );
+
+            report = new ReportEntry( testObject, getTestName( userFriendlyMethodName ), stringToPrint,
+                                      new PojoStackTraceWriter( testObject.getClass().getName(), method.getName(),
+                                                                t ) );
+
+            reportManager.testFailed( report );
+
+            // A return value of true indicates to this class's executeTestMethods
+            // method that it should abort and not attempt to execute
+            // any other test methods. The other caller of this method,
+            // TestRerunner.rerun, ignores this return value, because it is
+            // only running one test.
+            return true;
+        }
+
+        // A return value of false indicates to this class's executeTestMethods
+        // method that it should keep plowing ahead and invoke more test methods.
+        // The other caller of this method,
+        // TestRerunner.rerun, ignores this return value, because it is
+        // only running one test.
+        return false;
+    }
+
+    public String getTestName( String testMethodName )
+    {
+        if ( testMethodName == null )
+        {
+            throw new NullPointerException( "testMethodName is null" );
+        }
+
+        return getTestClass().getName() + "." + testMethodName;
+    }
+
+    public void setUpFixture()
+    {
+    }
+
+    public void tearDownFixture()
+    {
+    }
+
+    public int getTestCount()
+        throws TestSetFailedException
+    {
+        discoverTestMethods();
+
+        return testMethods.size();
+    }
+
+    private void discoverTestMethods()
+    {
+        if ( testMethods == null )
+        {
+            testMethods = new ArrayList();
+
+            Method[] methods = getTestClass().getMethods();
+
+            for ( int i = 0; i < methods.length; ++i )
+            {
+                Method m = methods[i];
+
+                if ( isValidTestMethod( m ) )
+                {
+                    String simpleName = m.getName();
+
+                    // name must have 5 or more chars
+                    if ( simpleName.length() > 4 )
+                    {
+                        String firstFour = simpleName.substring( 0, 4 );
+
+                        // name must start with "test"
+                        if ( firstFour.equals( TEST_METHOD_PREFIX ) )
+                        {
+                            testMethods.add( m );
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    private static boolean isValidTestMethod( Method m )
+    {
+        boolean isInstanceMethod = !Modifier.isStatic( m.getModifiers() );
+
+        boolean returnsVoid = m.getReturnType().equals( void.class );
+
+        boolean hasNoParams = m.getParameterTypes().length == 0;
+
+        return isInstanceMethod && returnsVoid && hasNoParams;
+    }
+}

Added: maven/sandbox/branches/surefire/surefire-collab2/surefire-api/src/main/java/org/apache/maven/surefire/testset/SurefireTestSet.java
URL: http://svn.apache.org/viewvc/maven/sandbox/branches/surefire/surefire-collab2/surefire-api/src/main/java/org/apache/maven/surefire/testset/SurefireTestSet.java?view=auto&rev=537691
==============================================================================
--- maven/sandbox/branches/surefire/surefire-collab2/surefire-api/src/main/java/org/apache/maven/surefire/testset/SurefireTestSet.java (added)
+++ maven/sandbox/branches/surefire/surefire-collab2/surefire-api/src/main/java/org/apache/maven/surefire/testset/SurefireTestSet.java Sun May 13 18:33:45 2007
@@ -0,0 +1,35 @@
+package org.apache.maven.surefire.testset;
+
+/*
+ * 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.report.ReporterManager;
+
+public interface SurefireTestSet
+{
+    void execute( ReporterManager reportManager, ClassLoader loader )
+        throws TestSetFailedException;
+
+    int getTestCount()
+        throws TestSetFailedException;
+
+    String getName();
+
+    Class getTestClass();
+}

Added: maven/sandbox/branches/surefire/surefire-collab2/surefire-api/src/main/java/org/apache/maven/surefire/testset/TestSetFailedException.java
URL: http://svn.apache.org/viewvc/maven/sandbox/branches/surefire/surefire-collab2/surefire-api/src/main/java/org/apache/maven/surefire/testset/TestSetFailedException.java?view=auto&rev=537691
==============================================================================
--- maven/sandbox/branches/surefire/surefire-collab2/surefire-api/src/main/java/org/apache/maven/surefire/testset/TestSetFailedException.java (added)
+++ maven/sandbox/branches/surefire/surefire-collab2/surefire-api/src/main/java/org/apache/maven/surefire/testset/TestSetFailedException.java Sun May 13 18:33:45 2007
@@ -0,0 +1,77 @@
+package org.apache.maven.surefire.testset;
+
+/*
+ * 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.NestedCheckedException;
+
+/**
+ * Exception that indicates a test failed.
+ *
+ * @author Bill Venners
+ */
+public class TestSetFailedException
+    extends NestedCheckedException
+{
+    /**
+     * Create a <code>TestFailedException</code> with no detail message.
+     */
+    public TestSetFailedException()
+    {
+    }
+
+    /**
+     * Create a <code>TestFailedException</code> with a detail message.
+     *
+     * @param message A detail message for this <code>TestFailedException</code>, or
+     *                <code>null</code>. If <code>null</code> is passed, the <code>getMessage</code>
+     *                method will return an empty <code>String</code>.
+     */
+    public TestSetFailedException( String message )
+    {
+        super( message );
+    }
+
+    /**
+     * Create a <code>TestFailedException</code> with the specified detail
+     * message and cause.
+     * <p/>
+     * <p>Note that the detail message associated with cause is
+     * <em>not</em> automatically incorporated in this throwable's detail
+     * message.
+     *
+     * @param message A detail message for this <code>TestFailedException</code>, or <code>null</code>.
+     * @param cause   the cause, which is saved for later retrieval by the <code>getCause</code> method.
+     *                (A null value is permitted, and indicates that the cause is nonexistent or unknown.)
+     */
+    public TestSetFailedException( String message, Throwable cause )
+    {
+        super( message, cause );
+    }
+
+    /**
+     * Create a <code>TestFailedException</code> with the specified cause.  The
+     * <code>getMessage</code> method of this exception object will return
+     * <code>(cause == null ? "" : cause.toString())</code>.
+     */
+    public TestSetFailedException( Throwable cause )
+    {
+        super( cause == null ? "" : cause.toString(), cause );
+    }
+}

Added: maven/sandbox/branches/surefire/surefire-collab2/surefire-api/src/main/java/org/apache/maven/surefire/util/NestedCheckedException.java
URL: http://svn.apache.org/viewvc/maven/sandbox/branches/surefire/surefire-collab2/surefire-api/src/main/java/org/apache/maven/surefire/util/NestedCheckedException.java?view=auto&rev=537691
==============================================================================
--- maven/sandbox/branches/surefire/surefire-collab2/surefire-api/src/main/java/org/apache/maven/surefire/util/NestedCheckedException.java (added)
+++ maven/sandbox/branches/surefire/surefire-collab2/surefire-api/src/main/java/org/apache/maven/surefire/util/NestedCheckedException.java Sun May 13 18:33:45 2007
@@ -0,0 +1,223 @@
+package org.apache.maven.surefire.util;
+
+/*
+ * 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.
+ */
+
+/*
+ * Copyright 2002-2005 the original author or authors.
+ *
+ * Licensed 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.io.PrintStream;
+import java.io.PrintWriter;
+
+/**
+ * <p>Copied from Spring framework to keep Java 1.3 compatability.</p>
+ * <p/>
+ * <p>Handy class for wrapping checked Exceptions with a root cause.</p>
+ * <p/>
+ * <p>This time-honored technique is no longer necessary in Java 1.4, which
+ * finally provides built-in support for exception nesting. Thus exceptions in
+ * applications written to use Java 1.4 need not extend this class. To ease
+ * migration, this class mirrors Java 1.4's nested exceptions as closely as possible.
+ * <p/>
+ * <p>Abstract to force the programmer to extend the class. <code>getMessage</code>
+ * will include nested exception information; <code>printStackTrace</code> etc will
+ * delegate to the wrapped exception, if any.
+ * <p/>
+ * <p>The similarity between this class and the NestedRuntimeException class is
+ * unavoidable, as Java forces these two classes to have different superclasses
+ * (ah, the inflexibility of concrete inheritance!).
+ * <p/>
+ * <p>As discussed in
+ * <a href="http://www.amazon.com/exec/obidos/tg/detail/-/0764543857/">Expert One-On-One J2EE Design and Development</a>,
+ * runtime exceptions are often a better alternative to checked exceptions.
+ * However, all exceptions should preserve their stack trace, if caused by a
+ * lower-level exception.
+ *
+ * @author Rod Johnson
+ * @author Juergen Hoeller
+ * @see #getMessage
+ * @see #printStackTrace
+ * @see NestedRuntimeException
+ */
+public class NestedCheckedException
+    extends Exception
+{
+
+    /**
+     * Root cause of this nested exception
+     */
+    private Throwable cause;
+
+    /**
+     * Construct a <code>NestedCheckedException</code> with no message or exception
+     */
+    public NestedCheckedException()
+    {
+        super();
+    }
+
+    /**
+     * Construct a <code>NestedCheckedException</code> with the specified detail message.
+     *
+     * @param msg the detail message
+     */
+    public NestedCheckedException( String msg )
+    {
+        super( msg );
+    }
+
+    /**
+     * Construct a <code>NestedCheckedException</code> with the specified detail message
+     * and nested exception.
+     *
+     * @param msg the detail message
+     * @param ex  the nested exception
+     */
+    public NestedCheckedException( String msg, Throwable ex )
+    {
+        super( msg );
+        this.cause = ex;
+    }
+
+    /**
+     * Construct a <code>NestedCheckedException</code> with the specified nested exception.
+     *
+     * @param ex the nested exception
+     */
+    public NestedCheckedException( Throwable ex )
+    {
+        super();
+        this.cause = ex;
+    }
+
+    /**
+     * Return the nested cause, or <code>null</code> if none.
+     */
+    public Throwable getCause()
+    {
+        // Even if you cannot set the cause of this exception other than through
+        // the constructor, we check for the cause being "this" here, as the cause
+        // could still be set to "this" via reflection: for example, by a remoting
+        // deserializer like Hessian's.
+        return ( this.cause == this ? null : this.cause );
+    }
+
+    /**
+     * Return the detail message, including the message from the nested exception
+     * if there is one.
+     */
+    public String getMessage()
+    {
+        if ( getCause() == null )
+        {
+            return super.getMessage();
+        }
+        else
+        {
+            return super.getMessage() + "; nested exception is " + getCause().getClass().getName() + ": " +
+                getCause().getMessage();
+        }
+    }
+
+    /**
+     * Print the composite message and the embedded stack trace to the specified stream.
+     *
+     * @param ps the print stream
+     */
+    public void printStackTrace( PrintStream ps )
+    {
+        if ( getCause() == null )
+        {
+            super.printStackTrace( ps );
+        }
+        else
+        {
+            ps.println( this );
+            getCause().printStackTrace( ps );
+        }
+    }
+
+    /**
+     * Print the composite message and the embedded stack trace to the specified print writer.
+     *
+     * @param pw the print writer
+     */
+    public void printStackTrace( PrintWriter pw )
+    {
+        if ( getCause() == null )
+        {
+            super.printStackTrace( pw );
+        }
+        else
+        {
+            pw.println( this );
+            getCause().printStackTrace( pw );
+        }
+    }
+
+    /**
+     * Check whether this exception contains an exception of the given class:
+     * either it is of the given class itself or it contains a nested cause
+     * of the given class.
+     * <p>Currently just traverses NestedCheckedException causes. Will use
+     * the JDK 1.4 exception cause mechanism once Spring requires JDK 1.4.
+     *
+     * @param exClass the exception class to look for
+     */
+    public boolean contains( Class exClass )
+    {
+        if ( exClass == null )
+        {
+            return false;
+        }
+        Throwable ex = this;
+        while ( ex != null )
+        {
+            if ( exClass.isInstance( ex ) )
+            {
+                return true;
+            }
+            if ( ex instanceof NestedCheckedException )
+            {
+                // Cast is necessary on JDK 1.3, where Throwable does not
+                // provide a "getCause" method itself.
+                ex = ( (NestedCheckedException) ex ).getCause();
+            }
+            else
+            {
+                ex = null;
+            }
+        }
+        return false;
+    }
+
+}

Added: maven/sandbox/branches/surefire/surefire-collab2/surefire-api/src/main/java/org/apache/maven/surefire/util/NestedRuntimeException.java
URL: http://svn.apache.org/viewvc/maven/sandbox/branches/surefire/surefire-collab2/surefire-api/src/main/java/org/apache/maven/surefire/util/NestedRuntimeException.java?view=auto&rev=537691
==============================================================================
--- maven/sandbox/branches/surefire/surefire-collab2/surefire-api/src/main/java/org/apache/maven/surefire/util/NestedRuntimeException.java (added)
+++ maven/sandbox/branches/surefire/surefire-collab2/surefire-api/src/main/java/org/apache/maven/surefire/util/NestedRuntimeException.java Sun May 13 18:33:45 2007
@@ -0,0 +1,241 @@
+package org.apache.maven.surefire.util;
+
+/*
+ * 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.
+ */
+
+/*
+ * Copyright 2002-2005 the original author or authors.
+ *
+ * Licensed 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.io.PrintStream;
+import java.io.PrintWriter;
+
+/**
+ * <p>Copied from Spring framework to keep Java 1.3 compatability.</p>
+ * <p/>
+ * <p>Handy class for wrapping runtime Exceptions with a root cause.</p>
+ * <p/>
+ * <p>This time-honoured technique is no longer necessary in Java 1.4, which
+ * finally provides built-in support for exception nesting. Thus exceptions in
+ * applications written to use Java 1.4 need not extend this class. To ease
+ * migration, this class mirrors Java 1.4's nested exceptions as closely as possible.
+ * <p/>
+ * <p>Abstract to force the programmer to extend the class. <code>getMessage</code>
+ * will include nested exception information; <code>printStackTrace</code> etc will
+ * delegate to the wrapped exception, if any.
+ * <p/>
+ * <p>The similarity between this class and the NestedCheckedException class is
+ * unavoidable, as Java forces these two classes to have different superclasses
+ * (ah, the inflexibility of concrete inheritance!).
+ * <p/>
+ * <p>As discussed in
+ * <a href="http://www.amazon.com/exec/obidos/tg/detail/-/0764543857/">Expert One-On-One J2EE Design and Development</a>,
+ * runtime exceptions are often a better alternative to checked exceptions.
+ * However, all exceptions should preserve their stack trace, if caused by a
+ * lower-level exception.
+ *
+ * @author Rod Johnson
+ * @author Juergen Hoeller
+ * @see #getMessage
+ * @see #printStackTrace
+ * @see NestedCheckedException
+ */
+public class NestedRuntimeException
+    extends RuntimeException
+{
+
+    /**
+     * Root cause of this nested exception
+     */
+    private Throwable cause;
+
+    /**
+     * Construct a <code>NestedRuntimeException</code> with no message or exception
+     */
+    public NestedRuntimeException()
+    {
+        super();
+    }
+
+    /**
+     * Construct a <code>NestedRuntimeException</code> with the specified detail message.
+     *
+     * @param msg the detail message
+     */
+    public NestedRuntimeException( String msg )
+    {
+        super( msg );
+    }
+
+    /**
+     * Construct a <code>NestedRuntimeException</code> with the specified detail message
+     * and nested exception.
+     *
+     * @param msg the detail message
+     * @param ex  the nested exception
+     */
+    public NestedRuntimeException( String msg, Throwable ex )
+    {
+        super( msg );
+        this.cause = ex;
+    }
+
+    /**
+     * Construct a <code>NestedRuntimeException</code> with the specified nested exception.
+     *
+     * @param ex the nested exception
+     */
+    public NestedRuntimeException( Throwable ex )
+    {
+        super();
+        this.cause = ex;
+    }
+
+    /**
+     * Return the nested cause, or <code>null</code> if none.
+     * <p>Note that this will only check one level of nesting.
+     * Use <code>getRootCause()</code> to retrieve the innermost cause.
+     *
+     * @see #getRootCause()
+     */
+    public Throwable getCause()
+    {
+        // Even if you cannot set the cause of this exception other than through
+        // the constructor, we check for the cause being "this" here, as the cause
+        // could still be set to "this" via reflection: for example, by a remoting
+        // deserializer like Hessian's.
+        return ( this.cause == this ? null : this.cause );
+    }
+
+    /**
+     * Return the detail message, including the message from the nested exception
+     * if there is one.
+     */
+    public String getMessage()
+    {
+        if ( getCause() == null )
+        {
+            return super.getMessage();
+        }
+        else
+        {
+            return super.getMessage() + "; nested exception is " + getCause().getClass().getName() + ": " +
+                getCause().getMessage();
+        }
+    }
+
+    /**
+     * Print the composite message and the embedded stack trace to the specified stream.
+     *
+     * @param ps the print stream
+     */
+    public void printStackTrace( PrintStream ps )
+    {
+        if ( getCause() == null )
+        {
+            super.printStackTrace( ps );
+        }
+        else
+        {
+            ps.println( this );
+            getCause().printStackTrace( ps );
+        }
+    }
+
+    /**
+     * Print the composite message and the embedded stack trace to the specified writer.
+     *
+     * @param pw the print writer
+     */
+    public void printStackTrace( PrintWriter pw )
+    {
+        if ( getCause() == null )
+        {
+            super.printStackTrace( pw );
+        }
+        else
+        {
+            pw.println( this );
+            getCause().printStackTrace( pw );
+        }
+    }
+
+    /**
+     * Retrieve the innermost cause of this exception, if any.
+     * <p>Currently just traverses NestedRuntimeException causes. Will use
+     * the JDK 1.4 exception cause mechanism once Spring requires JDK 1.4.
+     *
+     * @return the innermost exception, or <code>null</code> if none
+     */
+    public Throwable getRootCause()
+    {
+        Throwable cause = getCause();
+        if ( cause instanceof NestedRuntimeException )
+        {
+            return ( (NestedRuntimeException) cause ).getRootCause();
+        }
+        else
+        {
+            return cause;
+        }
+    }
+
+    /**
+     * Check whether this exception contains an exception of the given class:
+     * either it is of the given class itself or it contains a nested cause
+     * of the given class.
+     * <p>Currently just traverses NestedRuntimeException causes. Will use
+     * the JDK 1.4 exception cause mechanism once Spring requires JDK 1.4.
+     *
+     * @param exClass the exception class to look for
+     */
+    public boolean contains( Class exClass )
+    {
+        if ( exClass == null )
+        {
+            return false;
+        }
+        if ( exClass.isInstance( this ) )
+        {
+            return true;
+        }
+        Throwable cause = getCause();
+        if ( cause instanceof NestedRuntimeException )
+        {
+            return ( (NestedRuntimeException) cause ).contains( exClass );
+        }
+        else
+        {
+            return ( cause != null && exClass.isInstance( cause ) );
+        }
+    }
+
+}
\ No newline at end of file

Added: maven/sandbox/branches/surefire/surefire-collab2/surefire-api/src/main/java/org/apache/maven/surefire/util/PrettyPrintXMLWriter.java
URL: http://svn.apache.org/viewvc/maven/sandbox/branches/surefire/surefire-collab2/surefire-api/src/main/java/org/apache/maven/surefire/util/PrettyPrintXMLWriter.java?view=auto&rev=537691
==============================================================================
--- maven/sandbox/branches/surefire/surefire-collab2/surefire-api/src/main/java/org/apache/maven/surefire/util/PrettyPrintXMLWriter.java (added)
+++ maven/sandbox/branches/surefire/surefire-collab2/surefire-api/src/main/java/org/apache/maven/surefire/util/PrettyPrintXMLWriter.java Sun May 13 18:33:45 2007
@@ -0,0 +1,253 @@
+package org.apache.maven.surefire.util;
+
+/*
+ * 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.codehaus.plexus.util.StringUtils;
+import org.codehaus.plexus.util.xml.XMLWriter;
+
+import java.io.PrintWriter;
+import java.io.Writer;
+import java.util.LinkedList;
+
+public class PrettyPrintXMLWriter
+    implements XMLWriter
+{
+    private PrintWriter writer;
+
+    private LinkedList elementStack = new LinkedList();
+
+    private boolean tagInProgress;
+
+    private int depth;
+
+    private String lineIndenter;
+
+    private String encoding;
+
+    private String docType;
+
+    private boolean readyForNewLine;
+
+    private boolean tagIsEmpty;
+
+    public PrettyPrintXMLWriter( PrintWriter writer, String lineIndenter )
+    {
+        this( writer, lineIndenter, null, null );
+    }
+
+    public PrettyPrintXMLWriter( Writer writer, String lineIndenter )
+    {
+        this( new PrintWriter( writer ), lineIndenter );
+    }
+
+    public PrettyPrintXMLWriter( PrintWriter writer )
+    {
+        this( writer, null, null );
+    }
+
+    public PrettyPrintXMLWriter( Writer writer )
+    {
+        this( new PrintWriter( writer ) );
+    }
+
+    public PrettyPrintXMLWriter( PrintWriter writer, String lineIndenter, String encoding, String doctype )
+    {
+        this.writer = writer;
+
+        this.lineIndenter = lineIndenter;
+
+        this.encoding = encoding;
+
+        this.docType = doctype;
+
+        if ( docType != null || encoding != null )
+        {
+            writeDocumentHeaders();
+        }
+    }
+
+    public PrettyPrintXMLWriter( Writer writer, String lineIndenter, String encoding, String doctype )
+    {
+        this( new PrintWriter( writer ), lineIndenter, encoding, doctype );
+    }
+
+    public PrettyPrintXMLWriter( PrintWriter writer, String encoding, String doctype )
+    {
+        this( writer, "  ", encoding, doctype );
+    }
+
+    public PrettyPrintXMLWriter( Writer writer, String encoding, String doctype )
+    {
+        this( new PrintWriter( writer ), encoding, doctype );
+    }
+
+    public void startElement( String name )
+    {
+        tagIsEmpty = false;
+
+        finishTag();
+
+        write( "<" );
+
+        write( name );
+
+        elementStack.addLast( name );
+
+        tagInProgress = true;
+
+        depth++;
+
+        readyForNewLine = true;
+
+        tagIsEmpty = true;
+    }
+
+    public void writeText( String text )
+    {
+        writeText( text, true );
+    }
+
+    public void writeMarkup( String text )
+    {
+        writeText( text, false );
+    }
+
+    private void writeText( String text, boolean escapeXml )
+    {
+        readyForNewLine = false;
+
+        tagIsEmpty = false;
+
+        finishTag();
+
+        if ( escapeXml )
+        {
+            text = escapeXml( text );
+        }
+
+        write( text );
+    }
+
+    private static String escapeXml( String text )
+    {
+        text = StringUtils.replace( text, "&", "&amp;" );
+        text = StringUtils.replace( text, "<", "&amp;" );
+        text = StringUtils.replace( text, ">", "&amp;" );
+        text = StringUtils.replace( text, "\"", "&quot;" );
+        text = StringUtils.replace( text, "\'", "&apos;" );
+
+        return text;
+    }
+
+    public void addAttribute( String key, String value )
+    {
+        write( " " );
+
+        write( key );
+
+        write( "=\"" );
+
+        write( escapeXml( value ) );
+
+        write( "\"" );
+    }
+
+    public void endElement()
+    {
+        depth--;
+
+        if ( tagIsEmpty )
+        {
+            write( "/" );
+
+            readyForNewLine = false;
+
+            finishTag();
+
+            elementStack.removeLast();
+        }
+        else
+        {
+            finishTag();
+
+            write( "</" + elementStack.removeLast() + ">" );
+        }
+
+        readyForNewLine = true;
+    }
+
+    private void write( String str )
+    {
+        writer.write( str );
+    }
+
+    private void finishTag()
+    {
+        if ( tagInProgress )
+        {
+            write( ">" );
+        }
+
+        tagInProgress = false;
+
+        if ( readyForNewLine )
+        {
+            endOfLine();
+        }
+        readyForNewLine = false;
+
+        tagIsEmpty = false;
+    }
+
+    protected void endOfLine()
+    {
+        write( "\n" );
+
+        for ( int i = 0; i < depth; i++ )
+        {
+            write( lineIndenter );
+        }
+    }
+
+    private void writeDocumentHeaders()
+    {
+        write( "<?xml version=\"1.0\"" );
+
+        if ( encoding != null )
+        {
+            write( " encoding=\"" + encoding + "\"" );
+        }
+
+        write( "?>" );
+
+        endOfLine();
+
+        if ( docType != null )
+        {
+            write( "<!DOCTYPE " );
+
+            write( docType );
+
+            write( ">" );
+
+            endOfLine();
+        }
+    }
+}

Added: maven/sandbox/branches/surefire/surefire-collab2/surefire-api/src/main/java/org/apache/maven/surefire/util/TeeStream.java
URL: http://svn.apache.org/viewvc/maven/sandbox/branches/surefire/surefire-collab2/surefire-api/src/main/java/org/apache/maven/surefire/util/TeeStream.java?view=auto&rev=537691
==============================================================================
--- maven/sandbox/branches/surefire/surefire-collab2/surefire-api/src/main/java/org/apache/maven/surefire/util/TeeStream.java (added)
+++ maven/sandbox/branches/surefire/surefire-collab2/surefire-api/src/main/java/org/apache/maven/surefire/util/TeeStream.java Sun May 13 18:33:45 2007
@@ -0,0 +1,61 @@
+package org.apache.maven.surefire.util;
+
+/*
+ * 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.io.PrintStream;
+
+/**
+ * @author <a href="mailto:jason@maven.org">Jason van Zyl</a>
+ * @version $Id: TeeStream.java 514374 2007-03-04 08:48:42Z brett $
+ */
+public class TeeStream
+    extends PrintStream
+{
+    private PrintStream tee;
+
+    public TeeStream( PrintStream out1, PrintStream out2 )
+    {
+        super( out1 );
+
+        this.tee = out2;
+    }
+
+    public void write( byte[] buf, int off, int len )
+    {
+        super.write( buf, off, len );
+
+        tee.write( buf, off, len );
+    }
+
+    public void close()
+    {
+        super.close();
+
+        tee.close();
+    }
+
+    public void flush()
+    {
+        super.flush();
+
+        tee.flush();
+    }
+}
+

Added: maven/sandbox/branches/surefire/surefire-collab2/surefire-api/src/main/java/org/apache/maven/surefire/util/UrlUtils.java
URL: http://svn.apache.org/viewvc/maven/sandbox/branches/surefire/surefire-collab2/surefire-api/src/main/java/org/apache/maven/surefire/util/UrlUtils.java?view=auto&rev=537691
==============================================================================
--- maven/sandbox/branches/surefire/surefire-collab2/surefire-api/src/main/java/org/apache/maven/surefire/util/UrlUtils.java (added)
+++ maven/sandbox/branches/surefire/surefire-collab2/surefire-api/src/main/java/org/apache/maven/surefire/util/UrlUtils.java Sun May 13 18:33:45 2007
@@ -0,0 +1,94 @@
+package org.apache.maven.surefire.util;
+
+/*
+ * 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.io.File;
+import java.io.UnsupportedEncodingException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.BitSet;
+
+/**
+ * Utility for dealing with URLs in pre-JDK 1.4.
+ */
+public class UrlUtils
+{
+    private static final BitSet UNRESERVED = new BitSet( Byte.MAX_VALUE - Byte.MIN_VALUE + 1 );
+
+    private static final int RADIX = 16;
+
+    private static final int MASK = 0xf;
+
+    private UrlUtils()
+    {
+    }
+
+    private static final String ENCODING = "US-ASCII";
+
+    static
+    {
+        try
+        {
+            byte[] bytes =
+                "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.!~*'():/".getBytes( ENCODING );
+            for ( int i = 0; i < bytes.length; i++ )
+            {
+                UNRESERVED.set( bytes[i] );
+            }
+        }
+        catch ( UnsupportedEncodingException e )
+        {
+            // can't happen as US-ASCII must be present
+        }
+    }
+
+    public static URL getURL( File file )
+        throws MalformedURLException
+    {
+        URL url = file.toURL();
+        // encode any characters that do not comply with RFC 2396
+        // this is primarily to handle Windows where the user's home directory contains spaces
+        try
+        {
+            byte[] bytes = url.toString().getBytes( ENCODING );
+            StringBuffer buf = new StringBuffer( bytes.length );
+            for ( int i = 0; i < bytes.length; i++ )
+            {
+                byte b = bytes[i];
+                if ( UNRESERVED.get( b ) )
+                {
+                    buf.append( (char) b );
+                }
+                else
+                {
+                    buf.append( '%' );
+                    buf.append( Character.forDigit( b >>> 4 & MASK, RADIX ) );
+                    buf.append( Character.forDigit( b & MASK, RADIX ) );
+                }
+            }
+            return new URL( buf.toString() );
+        }
+        catch ( UnsupportedEncodingException e )
+        {
+            // should not happen as US-ASCII must be present
+            throw new NestedRuntimeException( e );
+        }
+    }
+}

Added: maven/sandbox/branches/surefire/surefire-collab2/surefire-api/src/main/resources/org/apache/maven/surefire/surefire.properties
URL: http://svn.apache.org/viewvc/maven/sandbox/branches/surefire/surefire-collab2/surefire-api/src/main/resources/org/apache/maven/surefire/surefire.properties?view=auto&rev=537691
==============================================================================
--- maven/sandbox/branches/surefire/surefire-collab2/surefire-api/src/main/resources/org/apache/maven/surefire/surefire.properties (added)
+++ maven/sandbox/branches/surefire/surefire-collab2/surefire-api/src/main/resources/org/apache/maven/surefire/surefire.properties Sun May 13 18:33:45 2007
@@ -0,0 +1,27 @@
+#
+# 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.
+#
+
+setupFixtureFailed=Method setupFixture threw an exception prior to calling {0}.
+cleanupFixtureFailed=Method cleanupFixture threw an exception prior to calling {0}.
+testSetStarting=Test set starting.
+testSetCompletedNormally=Test set completed.
+testStarting=Test starting.
+executeException=Exception occurred.
+testSuccessful=Test succeeded.
+testSkipped=Test skipped.
\ No newline at end of file

Added: maven/sandbox/branches/surefire/surefire-collab2/surefire-api/src/site/apt/index.apt
URL: http://svn.apache.org/viewvc/maven/sandbox/branches/surefire/surefire-collab2/surefire-api/src/site/apt/index.apt?view=auto&rev=537691
==============================================================================
--- maven/sandbox/branches/surefire/surefire-collab2/surefire-api/src/site/apt/index.apt (added)
+++ maven/sandbox/branches/surefire/surefire-collab2/surefire-api/src/site/apt/index.apt Sun May 13 18:33:45 2007
@@ -0,0 +1,33 @@
+ -----
+ Surefire API Design
+ -----
+ Brett Porter
+ -----
+ 3 March 2007
+ -----
+
+Surefire API
+
+* Definitions
+
+*-------------+-----------------------------------------------+
+| test method | individual test method within a class         |
+*-------------+-----------------------------------------------+
+| test        | 1..N test methods in 1 or more classes.       |
+*-------------+-----------------------------------------------+
+| suite       | 1..N tests.                                   |
+*-------------+-----------------------------------------------+
+| group       | A named subset of test methods within a test. |
+*-------------+-----------------------------------------------+
+
+  How each definition is applied depends on the provider, and the test suite being used.
+
+  Directory test suite: this constructs a single suite from a directory file set. Each discovered class is treated as a test.
+
+  TestNG XML test suite: this constructs a single suite from a testng.xml file. The definitions inside the file will match those above. 
+
+  JUnit 3.x: Groups are not supported.
+
+   See {{{providers/index.html}}} for more information on specific providers. |
+
+~~TODO: fix up URLs, move some to providers/javadoc.