http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/6a79127a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/log/PluginConsoleLogger.java
----------------------------------------------------------------------
diff --git 
a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/log/PluginConsoleLogger.java
 
b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/log/PluginConsoleLogger.java
new file mode 100644
index 0000000..7884270
--- /dev/null
+++ 
b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/log/PluginConsoleLogger.java
@@ -0,0 +1,126 @@
+package org.apache.maven.plugin.surefire.log;
+
+/*
+ * 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.plugin.logging.Log;
+import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
+
+/**
+ * Wrapper logger of miscellaneous (Maven 2.2.1 or 3.1) implementations of 
{@link Log}.
+ * Calling {@link Log#isInfoEnabled()} before {@link Log#info(CharSequence)} 
due to Maven 2.2.1.
+ *
+ * @author <a href="mailto:tibordig...@apache.org";>Tibor Digana (tibor17)</a>
+ * @since 2.19.2
+ * @see ConsoleLogger
+ */
+public final class PluginConsoleLogger
+    implements ConsoleLogger
+{
+    private final Log mojoLogger;
+
+    public PluginConsoleLogger( Log mojoLogger )
+    {
+        this.mojoLogger = mojoLogger;
+    }
+
+    public boolean isDebugEnabled()
+    {
+        return mojoLogger.isDebugEnabled();
+    }
+
+    public void debug( String message )
+    {
+        if ( mojoLogger.isDebugEnabled() )
+        {
+            mojoLogger.debug( message );
+        }
+    }
+
+    public void debug( CharSequence content, Throwable error )
+    {
+        if ( mojoLogger.isDebugEnabled() )
+        {
+            mojoLogger.debug( content, error );
+        }
+    }
+
+    public boolean isInfoEnabled()
+    {
+        return mojoLogger.isInfoEnabled();
+    }
+
+    public void info( String message )
+    {
+        if ( mojoLogger.isInfoEnabled() )
+        {
+            mojoLogger.info( message );
+        }
+    }
+
+    public boolean isWarnEnabled()
+    {
+        return mojoLogger.isWarnEnabled();
+    }
+
+    public void warning( String message )
+    {
+        if ( mojoLogger.isWarnEnabled() )
+        {
+            mojoLogger.warn( message );
+        }
+    }
+
+    public void warning( CharSequence content, Throwable error )
+    {
+        if ( mojoLogger.isWarnEnabled() )
+        {
+            mojoLogger.warn( content, error );
+        }
+    }
+
+    public boolean isErrorEnabled()
+    {
+        return mojoLogger.isErrorEnabled();
+    }
+
+    public void error( String message )
+    {
+        if ( mojoLogger.isErrorEnabled() )
+        {
+            mojoLogger.error( message );
+        }
+    }
+
+    public void error( String message, Throwable t )
+    {
+        if ( mojoLogger.isErrorEnabled() )
+        {
+            mojoLogger.error( message, t );
+        }
+    }
+
+    public void error( Throwable t )
+    {
+        if ( mojoLogger.isErrorEnabled() )
+        {
+            mojoLogger.error( t );
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/6a79127a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/ConsoleReporter.java
----------------------------------------------------------------------
diff --git 
a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/ConsoleReporter.java
 
b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/ConsoleReporter.java
index 1fcf4b0..36f311b 100644
--- 
a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/ConsoleReporter.java
+++ 
b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/ConsoleReporter.java
@@ -19,12 +19,16 @@ package org.apache.maven.plugin.surefire.report;
  * under the License.
  */
 
-import java.io.BufferedOutputStream;
-import java.io.OutputStreamWriter;
-import java.io.PrintStream;
-import java.io.PrintWriter;
 import java.util.List;
+
+import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
+import org.apache.maven.shared.utils.logging.MessageBuilder;
 import org.apache.maven.surefire.report.ReportEntry;
+import org.apache.maven.plugin.surefire.log.api.Level;
+
+import static org.apache.maven.plugin.surefire.log.api.Level.resolveLevel;
+import static 
org.apache.maven.plugin.surefire.report.TestSetStats.concatenateWithTestGroup;
+import static org.apache.maven.shared.utils.logging.MessageUtils.buffer;
 
 /**
  * Base class for console reporters.
@@ -40,63 +44,56 @@ public class ConsoleReporter
 
     private static final String TEST_SET_STARTING_PREFIX = "Running ";
 
-    private static final int BUFFER_SIZE = 4096;
+    private final ConsoleLogger logger;
 
-    private final PrintWriter writer;
-
-    public ConsoleReporter( PrintStream originalSystemOut )
-    {
-        OutputStreamWriter out = new OutputStreamWriter( new 
BufferedOutputStream( originalSystemOut, BUFFER_SIZE ) );
-        writer = new PrintWriter( out );
-    }
-
-    public void testSetStarting( ReportEntry report )
+    public ConsoleReporter( ConsoleLogger logger )
     {
-        writeMessage( getTestSetStartingMessage( report ) );
+        this.logger = logger;
     }
 
-    public void writeMessage( String message )
+    public ConsoleLogger getConsoleLogger()
     {
-        writer.print( message );
-        writer.flush();
+        return logger;
     }
 
-    public void writeLnMessage( String message )
+    public void testSetStarting( ReportEntry report )
     {
-        writer.println( message );
-        writer.flush();
+        MessageBuilder builder = buffer();
+        logger.info( concatenateWithTestGroup( builder.a( 
TEST_SET_STARTING_PREFIX ), report ) );
     }
 
     public void testSetCompleted( WrappedReportEntry report, TestSetStats 
testSetStats, List<String> testResults )
     {
-        writeMessage( testSetStats.getTestSetSummary( report ) );
-
-        if ( testResults != null )
+        boolean success = testSetStats.getCompletedCount() > 0;
+        boolean failures = testSetStats.getFailures() > 0;
+        boolean errors = testSetStats.getErrors() > 0;
+        boolean skipped = testSetStats.getSkipped() > 0;
+        boolean flakes = testSetStats.getSkipped() > 0;
+        Level level = resolveLevel( success, failures, errors, skipped, flakes 
);
+
+        println( testSetStats.getColoredTestSetSummary( report ), level );
+        for ( String testResult : testResults )
         {
-            for ( String testResult : testResults )
-            {
-                writeLnMessage( testResult );
-            }
+            println( testResult, level );
         }
     }
 
-
     public void reset()
     {
-        writer.flush();
     }
 
-    /**
-     * Get the test set starting message for a report.
-     * eg. "Running org.foo.BarTest ( of group )"
-     *
-     * @param report report whose test set is starting
-     * @return the message
-     */
-    private static String getTestSetStartingMessage( ReportEntry report )
+    private void println( String message, Level level )
     {
-        return TEST_SET_STARTING_PREFIX + report.getNameWithGroup() + "\n";
+        switch ( level )
+        {
+            case FAILURE:
+                logger.error( message );
+                break;
+            case UNSTABLE:
+                logger.warning( message );
+                break;
+            default:
+                logger.info( message );
+        }
     }
-
-
 }

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/6a79127a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/DefaultReporterFactory.java
----------------------------------------------------------------------
diff --git 
a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/DefaultReporterFactory.java
 
b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/DefaultReporterFactory.java
index e1ba809..0669763 100644
--- 
a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/DefaultReporterFactory.java
+++ 
b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/DefaultReporterFactory.java
@@ -21,12 +21,14 @@ package org.apache.maven.plugin.surefire.report;
 
 import org.apache.maven.plugin.surefire.StartupReportConfiguration;
 import org.apache.maven.plugin.surefire.runorder.StatisticsReporter;
-import org.apache.maven.surefire.report.ConsoleLogger;
-import org.apache.maven.surefire.report.DefaultDirectConsoleReporter;
+import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
+import org.apache.maven.plugin.surefire.log.api.NullConsoleLogger;
+import org.apache.maven.shared.utils.logging.MessageBuilder;
 import org.apache.maven.surefire.report.ReporterFactory;
 import org.apache.maven.surefire.report.RunListener;
 import org.apache.maven.surefire.report.RunStatistics;
 import org.apache.maven.surefire.report.StackTraceWriter;
+import org.apache.maven.plugin.surefire.log.api.Level;
 import org.apache.maven.surefire.suite.RunResult;
 
 import java.util.ArrayList;
@@ -47,6 +49,8 @@ import static 
org.apache.maven.plugin.surefire.report.DefaultReporterFactory.Tes
 import static org.apache.maven.plugin.surefire.report.ReportEntryType.ERROR;
 import static org.apache.maven.plugin.surefire.report.ReportEntryType.FAILURE;
 import static org.apache.maven.plugin.surefire.report.ReportEntryType.SUCCESS;
+import static org.apache.maven.plugin.surefire.log.api.Level.resolveLevel;
+import static org.apache.maven.shared.utils.logging.MessageUtils.buffer;
 
 /**
  * Provides reporting modules on the plugin side.
@@ -58,13 +62,12 @@ import static 
org.apache.maven.plugin.surefire.report.ReportEntryType.SUCCESS;
 public class DefaultReporterFactory
     implements ReporterFactory
 {
-    private RunStatistics globalStats = new RunStatistics();
-
     private final StartupReportConfiguration reportConfiguration;
-
+    private final ConsoleLogger consoleLogger;
     private final StatisticsReporter statisticsReporter;
+    private final Collection<TestSetRunListener> listeners;
 
-    private final Collection<TestSetRunListener> listeners = new 
ConcurrentLinkedQueue<TestSetRunListener>();
+    private RunStatistics globalStats = new RunStatistics();
 
     // from "<testclass>.<testmethod>" -> statistics about all the runs for 
flaky tests
     private Map<String, List<TestMethodStats>> flakyTests;
@@ -75,16 +78,19 @@ public class DefaultReporterFactory
     // from "<testclass>.<testmethod>" -> statistics about all the runs for 
error tests
     private Map<String, List<TestMethodStats>> errorTests;
 
-    public DefaultReporterFactory( StartupReportConfiguration 
reportConfiguration )
+    public DefaultReporterFactory( StartupReportConfiguration 
reportConfiguration, ConsoleLogger consoleLogger )
     {
         this.reportConfiguration = reportConfiguration;
+        this.consoleLogger = consoleLogger;
         statisticsReporter = 
reportConfiguration.instantiateStatisticsReporter();
+        listeners = new ConcurrentLinkedQueue<TestSetRunListener>();
     }
 
     public RunListener createReporter()
     {
+        ConsoleReporter consoleReporter = shouldReportToConsole() ? new 
ConsoleReporter( consoleLogger ) : null;
         TestSetRunListener testSetRunListener =
-            new TestSetRunListener( 
reportConfiguration.instantiateConsoleReporter(),
+            new TestSetRunListener( consoleReporter,
                                     
reportConfiguration.instantiateFileReporter(),
                                     
reportConfiguration.instantiateStatelessXmlReporter(),
                                     
reportConfiguration.instantiateConsoleOutputFileReporter(),
@@ -96,6 +102,13 @@ public class DefaultReporterFactory
         return testSetRunListener;
     }
 
+    private boolean shouldReportToConsole()
+    {
+        return reportConfiguration.isUseFile()
+                       ? reportConfiguration.isPrintSummary()
+                       : reportConfiguration.isRedirectTestOutputToFile() || 
reportConfiguration.isBriefOrPlainFormat();
+    }
+
     public void mergeFromOtherFactories( Collection<DefaultReporterFactory> 
factories )
     {
         for ( DefaultReporterFactory factory : factories )
@@ -123,38 +136,33 @@ public class DefaultReporterFactory
         return globalStats.getRunResult();
     }
 
-    private DefaultDirectConsoleReporter createConsoleLogger()
-    {
-        return new DefaultDirectConsoleReporter( 
reportConfiguration.getOriginalSystemOut() );
-    }
-
     public void runStarting()
     {
-        ConsoleLogger consoleReporter = createConsoleLogger();
-        consoleReporter.info( "" );
-        consoleReporter.info( 
"-------------------------------------------------------" );
-        consoleReporter.info( " T E S T S" );
-        consoleReporter.info( 
"-------------------------------------------------------" );
+        log( "" );
+        log( "-------------------------------------------------------" );
+        log( " T E S T S" );
+        log( "-------------------------------------------------------" );
     }
 
     private void runCompleted()
     {
-        final ConsoleLogger logger = createConsoleLogger();
         if ( reportConfiguration.isPrintSummary() )
         {
-            logger.info( "" );
-            logger.info( "Results:" );
-            logger.info( "" );
+            log( "" );
+            log( "Results:" );
+            log( "" );
         }
-        boolean printedFailures = printTestFailures( logger, failure );
-        printedFailures |= printTestFailures( logger, error );
-        printedFailures |= printTestFailures( logger, flake );
-        if ( printedFailures )
+        boolean printedFailures = printTestFailures( failure );
+        boolean printedErrors = printTestFailures( error );
+        boolean printedFlakes = printTestFailures( flake );
+        if ( printedFailures | printedErrors | printedFlakes )
         {
-            logger.info( "" );
+            log( "" );
         }
-        logger.info( globalStats.getSummary() );
-        logger.info( "" );
+        boolean hasSuccessful = globalStats.getCompletedCount() > 0;
+        boolean hasSkipped = globalStats.getSkipped() > 0;
+        log( globalStats.getSummary(), hasSuccessful, printedFailures, 
printedErrors, hasSkipped, printedFlakes );
+        log( "" );
     }
 
     public RunStatistics getGlobalRunStatistics()
@@ -168,7 +176,7 @@ public class DefaultReporterFactory
      */
     public static DefaultReporterFactory defaultNoXml()
     {
-        return new DefaultReporterFactory( 
StartupReportConfiguration.defaultNoXml() );
+        return new DefaultReporterFactory( 
StartupReportConfiguration.defaultNoXml(), new NullConsoleLogger() );
     }
 
     /**
@@ -318,24 +326,27 @@ public class DefaultReporterFactory
      * Print failed tests and flaked tests. A test is considered as a failed 
test if it failed/got an error with
      * all the runs. If a test passes in ever of the reruns, it will be count 
as a flaked test
      *
-     * @param logger the logger used to log information
      * @param type   the type of results to be printed, could be error, 
failure or flake
      * @return {@code true} if printed some lines
      */
     // Use default visibility for testing
-    boolean printTestFailures( ConsoleLogger logger, TestResultType type )
+    boolean printTestFailures( TestResultType type )
     {
         final Map<String, List<TestMethodStats>> testStats;
+        final Level level;
         switch ( type )
         {
             case failure:
                 testStats = failedTests;
+                level = Level.FAILURE;
                 break;
             case error:
                 testStats = errorTests;
+                level = Level.FAILURE;
                 break;
             case flake:
                 testStats = flakyTests;
+                level = Level.UNSTABLE;
                 break;
             default:
                 return false;
@@ -344,7 +355,7 @@ public class DefaultReporterFactory
         boolean printed = false;
         if ( !testStats.isEmpty() )
         {
-            logger.info( type.getLogPrefix() );
+            log( type.getLogPrefix(), level );
             printed = true;
         }
 
@@ -355,24 +366,24 @@ public class DefaultReporterFactory
             if ( testMethodStats.size() == 1 )
             {
                 // No rerun, follow the original output format
-                logger.info( "  " + testMethodStats.get( 0 
).getStackTraceWriter().smartTrimmedStackTrace() );
+                failure( "  " + testMethodStats.get( 0 
).getStackTraceWriter().smartTrimmedStackTrace() );
             }
             else
             {
-                logger.info( entry.getKey() );
+                log( entry.getKey(), level );
                 for ( int i = 0; i < testMethodStats.size(); i++ )
                 {
                     StackTraceWriter failureStackTrace = testMethodStats.get( 
i ).getStackTraceWriter();
                     if ( failureStackTrace == null )
                     {
-                        logger.info( "  Run " + ( i + 1 ) + ": PASS" );
+                        success( "  Run " + ( i + 1 ) + ": PASS" );
                     }
                     else
                     {
-                        logger.info( "  Run " + ( i + 1 ) + ": " + 
failureStackTrace.smartTrimmedStackTrace() );
+                        failure( "  Run " + ( i + 1 ) + ": " + 
failureStackTrace.smartTrimmedStackTrace() );
                     }
                 }
-                logger.info( "" );
+                log( "" );
             }
         }
         return printed;
@@ -401,4 +412,58 @@ public class DefaultReporterFactory
             return logPrefix;
         }
     }
+
+    private void log( String s, boolean success, boolean failures, boolean 
errors, boolean skipped, boolean flakes )
+    {
+        Level level = resolveLevel( success, failures, errors, skipped, flakes 
);
+        log( s, level );
+    }
+
+    private void log( String s, Level level )
+    {
+        MessageBuilder builder = buffer();
+        switch ( level )
+        {
+            case FAILURE:
+                consoleLogger.error( builder.failure( s ).toString() );
+                break;
+            case UNSTABLE:
+                consoleLogger.warning( builder.warning( s ).toString() );
+                break;
+            case SUCCESS:
+                consoleLogger.info( builder.info( s ).toString() );
+                break;
+            default:
+                consoleLogger.info( builder.a( s ).toString() );
+        }
+    }
+
+    private void log( String s )
+    {
+        consoleLogger.info( s );
+    }
+
+    private void info( String s )
+    {
+        MessageBuilder builder = buffer();
+        consoleLogger.info( builder.info( s ).toString() );
+    }
+
+    private void err( String s )
+    {
+        MessageBuilder builder = buffer();
+        consoleLogger.error( builder.error( s ).toString() );
+    }
+
+    private void success( String s )
+    {
+        MessageBuilder builder = buffer();
+        consoleLogger.info( builder.success( s ).toString() );
+    }
+
+    private void failure( String s )
+    {
+        MessageBuilder builder = buffer();
+        consoleLogger.error( builder.failure( s ).toString() );
+    }
 }

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/6a79127a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/DirectConsoleOutput.java
----------------------------------------------------------------------
diff --git 
a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/DirectConsoleOutput.java
 
b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/DirectConsoleOutput.java
index bd5ba7c..25312aa 100644
--- 
a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/DirectConsoleOutput.java
+++ 
b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/DirectConsoleOutput.java
@@ -23,10 +23,11 @@ import java.io.PrintStream;
 import java.nio.ByteBuffer;
 import java.nio.CharBuffer;
 import java.nio.charset.CharacterCodingException;
-import java.nio.charset.Charset;
 
 import org.apache.maven.surefire.report.ReportEntry;
 
+import static java.nio.charset.Charset.defaultCharset;
+
 /**
  * Outputs test system out/system err directly to the console
  * <p/>
@@ -51,11 +52,10 @@ public class DirectConsoleOutput
 
     public void writeTestOutput( byte[] buf, int off, int len, boolean stdout )
     {
-        PrintStream stream = stdout ? sout : serr;
-
+        final PrintStream stream = stdout ? sout : serr;
         try
         {
-            CharBuffer decode = Charset.defaultCharset().newDecoder().decode( 
ByteBuffer.wrap( buf, off, len ) );
+            CharBuffer decode = defaultCharset().newDecoder().decode( 
ByteBuffer.wrap( buf, off, len ) );
             stream.append( decode );
         }
         catch ( CharacterCodingException e )
@@ -74,6 +74,5 @@ public class DirectConsoleOutput
 
     public void close()
     {
-        //To change body of implemented methods use File | Settings | File 
Templates.
     }
 }

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/6a79127a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/FileReporter.java
----------------------------------------------------------------------
diff --git 
a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/FileReporter.java
 
b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/FileReporter.java
index 2e862cf..a4d8c8e 100644
--- 
a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/FileReporter.java
+++ 
b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/FileReporter.java
@@ -29,6 +29,7 @@ import java.io.PrintWriter;
 import java.util.List;
 
 import static 
org.apache.maven.plugin.surefire.report.FileReporterUtils.stripIllegalFilenameChars;
+import static org.apache.maven.surefire.util.internal.StringUtils.isNotBlank;
 
 /**
  * Base class for file reporters.
@@ -40,14 +41,11 @@ public class FileReporter
 {
     private final File reportsDirectory;
 
-    private final boolean deleteOnStarting;
-
     private final String reportNameSuffix;
 
     public FileReporter( File reportsDirectory, String reportNameSuffix )
     {
         this.reportsDirectory = reportsDirectory;
-        this.deleteOnStarting = false;
         this.reportNameSuffix = reportNameSuffix;
     }
 
@@ -60,12 +58,6 @@ public class FileReporter
         // noinspection ResultOfMethodCallIgnored
         reportDir.mkdirs();
 
-        if ( deleteOnStarting && reportFile.exists() )
-        {
-            // noinspection ResultOfMethodCallIgnored
-            reportFile.delete();
-        }
-
         try
         {
             PrintWriter writer = new PrintWriter( new FileWriter( reportFile ) 
);
@@ -87,19 +79,9 @@ public class FileReporter
     public static File getReportFile( File reportsDirectory, String 
reportEntryName, String reportNameSuffix,
                                       String fileExtension )
     {
-        File reportFile;
-
-        if ( reportNameSuffix != null && reportNameSuffix.length() > 0 )
-        {
-            reportFile =
-                new File( reportsDirectory, stripIllegalFilenameChars( 
reportEntryName + "-" + reportNameSuffix
-                    + fileExtension ) );
-        }
-        else
-        {
-            reportFile = new File( reportsDirectory, 
stripIllegalFilenameChars( reportEntryName + fileExtension ) );
-        }
-        return reportFile;
+        String fileName =
+                reportEntryName + ( isNotBlank( reportNameSuffix ) ? "-" + 
reportNameSuffix : "" ) + fileExtension;
+        return new File( reportsDirectory, stripIllegalFilenameChars( fileName 
) );
     }
 
     public void testSetCompleted( WrappedReportEntry report, TestSetStats 
testSetStats, List<String> testResults )
@@ -107,16 +89,11 @@ public class FileReporter
         PrintWriter writer = testSetStarting( report );
         try
         {
-            writer.print( testSetStats.getTestSetSummary( report ) );
-
-            if ( testResults != null )
+            writer.println( testSetStats.getTestSetSummary( report ) );
+            for ( String testResult : testResults )
             {
-                for ( String testResult : testResults )
-                {
-                    writer.println( testResult );
-                }
+                writer.println( testResult );
             }
-
             writer.flush();
         }
         finally

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/6a79127a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/TestSetRunListener.java
----------------------------------------------------------------------
diff --git 
a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/TestSetRunListener.java
 
b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/TestSetRunListener.java
index 2bb187f..4b12884 100644
--- 
a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/TestSetRunListener.java
+++ 
b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/TestSetRunListener.java
@@ -21,14 +21,20 @@ package org.apache.maven.plugin.surefire.report;
 
 import java.io.IOException;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 
 import org.apache.maven.plugin.surefire.runorder.StatisticsReporter;
-import org.apache.maven.surefire.report.ConsoleLogger;
+import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
 import org.apache.maven.surefire.report.ConsoleOutputReceiver;
 import org.apache.maven.surefire.report.ReportEntry;
 import org.apache.maven.surefire.report.RunListener;
 
+import static org.apache.maven.plugin.surefire.report.ReportEntryType.ERROR;
+import static org.apache.maven.plugin.surefire.report.ReportEntryType.FAILURE;
+import static org.apache.maven.plugin.surefire.report.ReportEntryType.SKIPPED;
+import static org.apache.maven.plugin.surefire.report.ReportEntryType.SUCCESS;
+
 /**
  * Reports data for a single test set.
  * <p/>
@@ -76,15 +82,55 @@ public class TestSetRunListener
         this.simpleXMLReporter = simpleXMLReporter;
         this.consoleOutputReceiver = consoleOutputReceiver;
         this.briefOrPlainFormat = briefOrPlainFormat;
-        this.detailsForThis = new TestSetStats( trimStackTrace, isPlainFormat 
);
-        this.testMethodStats = new ArrayList<TestMethodStats>(  );
+        detailsForThis = new TestSetStats( trimStackTrace, isPlainFormat );
+        testMethodStats = new ArrayList<TestMethodStats>();
+    }
+
+    public void debug( String message )
+    {
+        if ( consoleReporter != null )
+        {
+            consoleReporter.getConsoleLogger().debug( trimTrailingNewLine( 
message ) );
+        }
     }
 
     public void info( String message )
     {
         if ( consoleReporter != null )
         {
-            consoleReporter.writeMessage( message );
+            consoleReporter.getConsoleLogger().info( trimTrailingNewLine( 
message ) );
+        }
+    }
+
+    public void warning( String message )
+    {
+        if ( consoleReporter != null )
+        {
+            consoleReporter.getConsoleLogger().warning( trimTrailingNewLine( 
message ) );
+        }
+    }
+
+    public void error( String message )
+    {
+        if ( consoleReporter != null )
+        {
+            consoleReporter.getConsoleLogger().error( trimTrailingNewLine( 
message ) );
+        }
+    }
+
+    public void error( String message, Throwable t )
+    {
+        if ( consoleReporter != null )
+        {
+            consoleReporter.getConsoleLogger().error( message, t );
+        }
+    }
+
+    public void error( Throwable t )
+    {
+        if ( consoleReporter != null )
+        {
+            consoleReporter.getConsoleLogger().error( t );
         }
     }
 
@@ -126,8 +172,9 @@ public class TestSetRunListener
 
     public void testSetCompleted( ReportEntry report )
     {
-        WrappedReportEntry wrap = wrapTestSet( report );
-        List<String> testResults = briefOrPlainFormat ? 
detailsForThis.getTestResults() : null;
+        final WrappedReportEntry wrap = wrapTestSet( report );
+        final List<String> testResults =
+                briefOrPlainFormat ? detailsForThis.getTestResults() : 
Collections.<String>emptyList();
         if ( fileReporter != null )
         {
             fileReporter.testSetCompleted( wrap, detailsForThis, testResults );
@@ -169,7 +216,7 @@ public class TestSetRunListener
 
     public void testSucceeded( ReportEntry reportEntry )
     {
-        WrappedReportEntry wrapped = wrap( reportEntry, 
ReportEntryType.SUCCESS );
+        WrappedReportEntry wrapped = wrap( reportEntry, SUCCESS );
         detailsForThis.testSucceeded( wrapped );
         if ( statisticsReporter != null )
         {
@@ -180,7 +227,7 @@ public class TestSetRunListener
 
     public void testError( ReportEntry reportEntry )
     {
-        WrappedReportEntry wrapped = wrap( reportEntry, ReportEntryType.ERROR 
);
+        WrappedReportEntry wrapped = wrap( reportEntry, ERROR );
         detailsForThis.testError( wrapped );
         if ( statisticsReporter != null )
         {
@@ -191,7 +238,7 @@ public class TestSetRunListener
 
     public void testFailed( ReportEntry reportEntry )
     {
-        WrappedReportEntry wrapped = wrap( reportEntry, 
ReportEntryType.FAILURE );
+        WrappedReportEntry wrapped = wrap( reportEntry, FAILURE );
         detailsForThis.testFailure( wrapped );
         if ( statisticsReporter != null )
         {
@@ -206,7 +253,7 @@ public class TestSetRunListener
 
     public void testSkipped( ReportEntry reportEntry )
     {
-        WrappedReportEntry wrapped = wrap( reportEntry, 
ReportEntryType.SKIPPED );
+        WrappedReportEntry wrapped = wrap( reportEntry, SKIPPED );
 
         detailsForThis.testSkipped( wrapped );
         if ( statisticsReporter != null )
@@ -228,7 +275,7 @@ public class TestSetRunListener
     private WrappedReportEntry wrap( ReportEntry other, ReportEntryType 
reportEntryType )
     {
         final int estimatedElapsed;
-        if ( reportEntryType != ReportEntryType.SKIPPED )
+        if ( reportEntryType != SKIPPED )
         {
             if ( other.getElapsed() != null )
             {
@@ -277,4 +324,15 @@ public class TestSetRunListener
     {
         return testMethodStats;
     }
+
+    public static String trimTrailingNewLine( final String message )
+    {
+        final int e = message == null ? 0 : lineBoundSymbolWidth( message );
+        return message != null && e != 0 ? message.substring( 0, 
message.length() - e ) : message;
+    }
+
+    private static int lineBoundSymbolWidth( String message )
+    {
+        return message.endsWith( "\n" ) || message.endsWith( "\r" ) ? 1 : ( 
message.endsWith( "\r\n" ) ? 2 : 0 );
+    }
 }

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/6a79127a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/TestSetStats.java
----------------------------------------------------------------------
diff --git 
a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/TestSetStats.java
 
b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/TestSetStats.java
index e5b47d2..9fcfafe 100644
--- 
a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/TestSetStats.java
+++ 
b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/TestSetStats.java
@@ -19,17 +19,32 @@ package org.apache.maven.plugin.surefire.report;
  * under the License.
  */
 
+import org.apache.maven.shared.utils.logging.MessageBuilder;
+import org.apache.maven.surefire.report.ReportEntry;
+
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
 import java.util.Queue;
 import java.util.concurrent.ConcurrentLinkedQueue;
 
+import static org.apache.maven.shared.utils.logging.MessageUtils.buffer;
+
 /**
  * Maintains per-thread test result state. Not thread safe.
  */
 public class TestSetStats
 {
+    private static final String TESTS = "Tests ";
+    private static final String RUN = "run: ";
+    private static final String TESTS_RUN = "Tests run: ";
+    private static final String FAILURES = "Failures: ";
+    private static final String ERRORS = "Errors: ";
+    private static final String SKIPPED = "Skipped: ";
+    private static final String FAILURE_MARKER = " <<< FAILURE!";
+    private static final String IN_MARKER = " - in ";
+    private static final String COMMA = ", ";
+
     private final Queue<WrappedReportEntry> reportEntries = new 
ConcurrentLinkedQueue<WrappedReportEntry>();
 
     private final boolean trimStackTrace;
@@ -164,21 +179,100 @@ public class TestSetStats
 
     public String getTestSetSummary( WrappedReportEntry reportEntry )
     {
-        String summary =
-                "Tests run: " + completedCount
-                        +  ", Failures: " + failures + ", Errors: " + errors + 
", Skipped: " + skipped + ", "
-                        + reportEntry.getElapsedTimeVerbose();
+        String summary = TESTS_RUN + completedCount
+                                 + COMMA
+                                 + FAILURES + failures
+                                 + COMMA
+                                 + ERRORS + errors
+                                 + COMMA
+                                 + SKIPPED + skipped
+                                 + COMMA
+                                 + reportEntry.getElapsedTimeVerbose();
 
         if ( failures > 0 || errors > 0 )
         {
-            summary += " <<< FAILURE!";
+            summary += FAILURE_MARKER;
         }
 
-        summary += " - in " + reportEntry.getNameWithGroup() + "\n";
+        summary += IN_MARKER + reportEntry.getNameWithGroup();
 
         return summary;
     }
 
+    public String getColoredTestSetSummary( WrappedReportEntry reportEntry )
+    {
+        final boolean isSuccessful = failures == 0 && errors == 0 && skipped 
== 0;
+        final boolean isFailure = failures > 0;
+        final boolean isError = errors > 0;
+        final boolean isFailureOrError = isFailure | isError;
+        final boolean isSkipped = skipped > 0;
+        final  MessageBuilder builder = buffer();
+        if ( isSuccessful )
+        {
+            if ( completedCount == 0 )
+            {
+                builder.strong( TESTS_RUN ).strong( completedCount );
+            }
+            else
+            {
+                builder.success( TESTS_RUN ).success( completedCount );
+            }
+        }
+        else
+        {
+            if ( isFailureOrError )
+            {
+                builder.failure( TESTS ).strong( RUN ).strong( completedCount 
);
+            }
+            else
+            {
+                builder.warning( TESTS ).strong( RUN ).strong( completedCount 
);
+            }
+        }
+        builder.a( COMMA );
+        if ( isFailure )
+        {
+            builder.failure( FAILURES ).failure( failures );
+        }
+        else
+        {
+            builder.a( FAILURES ).a( failures );
+        }
+        builder.a( COMMA );
+        if ( isError )
+        {
+            builder.failure( ERRORS ).failure( errors );
+        }
+        else
+        {
+            builder.a( ERRORS ).a( errors );
+        }
+        builder.a( COMMA );
+        if ( isSkipped )
+        {
+            builder.failure( SKIPPED ).failure( skipped );
+        }
+        else
+        {
+            builder.a( SKIPPED ).a( skipped );
+        }
+        builder.a( COMMA )
+                .a( reportEntry.getElapsedTimeVerbose() );
+        if ( isFailureOrError )
+        {
+            if ( isFailure )
+            {
+                builder.failure( FAILURE_MARKER );
+            }
+            else
+            {
+                builder.warning( FAILURE_MARKER );
+            }
+        }
+        builder.a( IN_MARKER );
+        return concatenateWithTestGroup( builder, reportEntry );
+    }
+
     public List<String> getTestResults()
     {
         List<String> result = new ArrayList<String>();
@@ -204,4 +298,23 @@ public class TestSetStats
     {
         return reportEntries;
     }
+
+    /**
+     * Append the test set message for a report.
+     * e.g. "org.foo.BarTest ( of group )"
+     *
+     * @param builder    MessageBuilder with preceded text inside
+     * @param report     report whose test set is starting
+     * @return the message
+     */
+    public static String concatenateWithTestGroup( MessageBuilder builder, 
ReportEntry report )
+    {
+        final String testClass = report.getNameWithGroup();
+        int delimiter = testClass.lastIndexOf( '.' );
+        String pkg = testClass.substring( 0, 1 + delimiter );
+        String cls = testClass.substring( 1 + delimiter );
+        return builder.a( pkg )
+                       .strong( cls )
+                       .toString();
+    }
 }

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/6a79127a/maven-surefire-common/src/main/java/org/apache/maven/surefire/report/RunStatistics.java
----------------------------------------------------------------------
diff --git 
a/maven-surefire-common/src/main/java/org/apache/maven/surefire/report/RunStatistics.java
 
b/maven-surefire-common/src/main/java/org/apache/maven/surefire/report/RunStatistics.java
index bcd5286..29aa8dc 100644
--- 
a/maven-surefire-common/src/main/java/org/apache/maven/surefire/report/RunStatistics.java
+++ 
b/maven-surefire-common/src/main/java/org/apache/maven/surefire/report/RunStatistics.java
@@ -37,10 +37,6 @@ public class RunStatistics
 
     private int flakes;
 
-    public RunStatistics()
-    {
-    }
-
     public synchronized boolean hadFailures()
     {
         return failures > 0;

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/6a79127a/maven-surefire-common/src/main/java/org/apache/maven/surefire/spi/ServiceLoader.java
----------------------------------------------------------------------
diff --git 
a/maven-surefire-common/src/main/java/org/apache/maven/surefire/spi/ServiceLoader.java
 
b/maven-surefire-common/src/main/java/org/apache/maven/surefire/spi/ServiceLoader.java
new file mode 100644
index 0000000..c23e5d6
--- /dev/null
+++ 
b/maven-surefire-common/src/main/java/org/apache/maven/surefire/spi/ServiceLoader.java
@@ -0,0 +1,171 @@
+package org.apache.maven.surefire.spi;
+
+/*
+ * 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 javax.annotation.Nonnull;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.lang.reflect.InvocationTargetException;
+import java.net.URL;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Set;
+
+import static java.lang.Character.isJavaIdentifierPart;
+import static java.lang.Character.isJavaIdentifierStart;
+import static java.util.Collections.emptySet;
+import static org.apache.maven.surefire.util.ReflectionUtils.getConstructor;
+
+/**
+ * SPI loader for Java 1.5.
+ *
+ * @since 2.19.2
+ */
+public class ServiceLoader
+{
+
+    @Nonnull
+    @SuppressWarnings( "unchecked" )
+    public <T> Set<T> load( Class<T> clazz, ClassLoader classLoader )
+    {
+        try
+        {
+            Set<T> implementations = new HashSet<T>();
+            for ( String fullyQualifiedClassName : lookup( clazz, classLoader 
) )
+            {
+                Class<?> implClass = classLoader.loadClass( 
fullyQualifiedClassName );
+                implementations.add( (T) getConstructor( implClass 
).newInstance() );
+            }
+            return implementations;
+        }
+        catch ( IOException e )
+        {
+            throw new IllegalStateException( e.getLocalizedMessage(), e );
+        }
+        catch ( ClassNotFoundException e )
+        {
+            throw new IllegalStateException( e.getLocalizedMessage(), e );
+        }
+        catch ( InvocationTargetException e )
+        {
+            throw new IllegalStateException( e.getLocalizedMessage(), e );
+        }
+        catch ( InstantiationException e )
+        {
+            throw new IllegalStateException( e.getLocalizedMessage(), e );
+        }
+        catch ( IllegalAccessException e )
+        {
+            throw new IllegalStateException( e.getLocalizedMessage(), e );
+        }
+    }
+
+    @Nonnull
+    public Set<String> lookup( Class<?> clazz, ClassLoader classLoader )
+            throws IOException
+    {
+        final String resourceName = "META-INF/services/" + clazz.getName();
+
+        if ( classLoader == null )
+        {
+            return emptySet();
+        }
+        final Enumeration<URL> urls = classLoader.getResources( resourceName );
+        return lookupSpiImplementations( urls );
+    }
+
+    /**
+     * Method loadServices loads the services of a class that are
+     * defined using the SPI mechanism.
+     *
+     * @param urlEnumeration The urls from the resource
+     * @return The set of service provider names
+     * @throws IOException When reading the streams fails
+     */
+    @Nonnull
+    @SuppressWarnings( "checkstyle:innerassignment" )
+    private static Set<String> lookupSpiImplementations( final 
Enumeration<URL> urlEnumeration )
+            throws IOException
+    {
+        final Set<String> names = new HashSet<String>();
+        nextUrl:
+        while ( urlEnumeration.hasMoreElements() )
+        {
+            final URL url = urlEnumeration.nextElement();
+            final BufferedReader reader = getReader( url );
+            try
+            {
+                for ( String line; ( line = reader.readLine() ) != null; )
+                {
+                    int ci = line.indexOf( '#' );
+                    if ( ci >= 0 )
+                    {
+                        line = line.substring( 0, ci );
+                    }
+                    line = line.trim();
+                    int n = line.length();
+                    if ( n == 0 )
+                    {
+                        continue; // next line
+                    }
+
+                    if ( line.indexOf( ' ' ) >= 0 || line.indexOf( '\t' ) >= 0 
)
+                    {
+                        continue nextUrl; // next url
+                    }
+                    char cp = line.charAt( 0 ); // should use codePointAt but 
this was JDK1.3
+                    if ( !isJavaIdentifierStart( cp ) )
+                    {
+                        continue nextUrl; // next url
+                    }
+                    for ( int i = 1; i < n; i++ )
+                    {
+                        cp = line.charAt( i );  // should use codePointAt but 
this was JDK1.3
+                        if ( !isJavaIdentifierPart( cp ) && cp != '.' )
+                        {
+                            continue nextUrl; // next url
+                        }
+                    }
+                    if ( !names.contains( line ) )
+                    {
+                        names.add( line );
+                    }
+                }
+            }
+            finally
+            {
+                reader.close();
+            }
+        }
+
+        return names;
+    }
+
+    @Nonnull
+    private static BufferedReader getReader( @Nonnull URL url )
+            throws IOException
+    {
+        final InputStream inputStream = url.openStream();
+        final InputStreamReader inputStreamReader = new InputStreamReader( 
inputStream );
+        return new BufferedReader( inputStreamReader );
+    }
+}

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/6a79127a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/SurefireReflectorTest.java
----------------------------------------------------------------------
diff --git 
a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/SurefireReflectorTest.java
 
b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/SurefireReflectorTest.java
new file mode 100644
index 0000000..cd31d34
--- /dev/null
+++ 
b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/SurefireReflectorTest.java
@@ -0,0 +1 @@
+package org.apache.maven.plugin.surefire;

/*
 * 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.plugin.surefire.log.api.ConsoleLogger;
import org.apache.maven.plugin.surefire.log.api.ConsoleLoggerDecorator;
import o
 rg.apache.maven.plugin.surefire.log.api.PrintStreamLogger;
import org.apache.maven.surefire.booter.IsolatedClassLoader;
import org.apache.maven.surefire.booter.SurefireReflector;
import org.junit.Before;
import org.junit.Test;

import static org.apache.maven.surefire.util.ReflectionUtils.getMethod;
import static 
org.apache.maven.surefire.util.ReflectionUtils.invokeMethodWithArray;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.CoreMatchers.sameInstance;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;

/**
 * @author <a href="mailto:tibordig...@apache.org";>Tibor Digana (tibor17)</a>
 * @see ConsoleLogger
 * @see SurefireReflector
 * @since 2.19.2
 */
public class SurefireReflectorTest
{
     private ConsoleLogger logger;
    private SurefireReflector reflector;

    @Before
    public void prepareData()
    {
        logger = spy( new PrintStreamLogger( System.out ) );
        ClassLoader cl = new IsolatedClassLoader( 
Thread.currentThread().getContextClassLoader(), false, "role" );
        reflector = new SurefireReflector( cl );
    }

    @Test
    public void shouldProxyConsoleLogger()
    {
        Object mirror = reflector.createConsoleLogger( logger );
        assertThat( mirror, is( notNullValue() ) );
        assertThat( mirror.getClass().getInterfaces()[0].getName(), is( 
ConsoleLogger.class.getName() ) );
        assertThat( mirror, is( not( sameInstance( (Object) logger ) ) ) );
        assertThat( mirror, is( instanceOf( ConsoleLoggerDecorator.class ) ) );
        invokeMethodWithArray( mirror, getMethod( mirror, "info", String.class 
), "Hi There!" );
        verify( logger, times( 1 ) ).info( "Hi There!" );
    }
}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/6a79127a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/ForkingRunListenerTest.java
----------------------------------------------------------------------
diff --git 
a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/ForkingRunListenerTest.java
 
b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/ForkingRunListenerTest.java
index 63d3417..cccb14b 100644
--- 
a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/ForkingRunListenerTest.java
+++ 
b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/ForkingRunListenerTest.java
@@ -30,7 +30,8 @@ import java.util.StringTokenizer;
 import org.apache.maven.plugin.surefire.booterclient.output.ForkClient;
 import org.apache.maven.surefire.booter.ForkingRunListener;
 import org.apache.maven.surefire.report.CategorizedReportEntry;
-import org.apache.maven.surefire.report.ConsoleLogger;
+import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
+import org.apache.maven.plugin.surefire.log.api.NullConsoleLogger;
 import org.apache.maven.surefire.report.ConsoleOutputReceiver;
 import org.apache.maven.surefire.report.LegacyPojoStackTraceWriter;
 import org.apache.maven.surefire.report.ReportEntry;
@@ -220,8 +221,9 @@ public class ForkingRunListenerTest
 
         TestSetMockReporterFactory providerReporterFactory = new 
TestSetMockReporterFactory();
         final Properties testVmSystemProperties = new Properties();
+        NullConsoleLogger log = new NullConsoleLogger();
         ForkClient forkStreamClient = new ForkClient( providerReporterFactory, 
testVmSystemProperties,
-                                                      new 
MockNotifiableTestStream() );
+                                                      new 
MockNotifiableTestStream(), log );
 
         forkStreamClient.consumeMultiLineContent( content.toString( "utf-8" ) 
);
 
@@ -245,8 +247,9 @@ public class ForkingRunListenerTest
         forkingReporter.testSetCompleted( reportEntry );
 
         TestSetMockReporterFactory providerReporterFactory = new 
TestSetMockReporterFactory();
+        NullConsoleLogger log = new NullConsoleLogger();
         ForkClient forkStreamClient = new ForkClient( providerReporterFactory, 
new Properties(),
-                                                      new 
MockNotifiableTestStream() );
+                                                      new 
MockNotifiableTestStream(), log );
 
         forkStreamClient.consumeMultiLineContent( content.toString( "utf-8" ) 
);
 
@@ -269,8 +272,9 @@ public class ForkingRunListenerTest
         new ForkingRunListener( printStream, anotherChannel, false 
).testSkipped( secondExpected );
 
         TestSetMockReporterFactory providerReporterFactory = new 
TestSetMockReporterFactory();
+        NullConsoleLogger log = new NullConsoleLogger();
         final ForkClient forkStreamClient = new ForkClient( 
providerReporterFactory, new Properties(),
-                                                            new 
MockNotifiableTestStream() );
+                                                            new 
MockNotifiableTestStream(), log );
         forkStreamClient.consumeMultiLineContent( content.toString( "utf-8" ) 
);
 
         MockReporter reporter = (MockReporter) forkStreamClient.getReporter( 
defaultChannel );
@@ -344,8 +348,9 @@ public class ForkingRunListenerTest
             throws ReporterException, IOException
         {
             TestSetMockReporterFactory providerReporterFactory = new 
TestSetMockReporterFactory();
+            NullConsoleLogger log = new NullConsoleLogger();
             final ForkClient forkStreamClient = new ForkClient( 
providerReporterFactory, new Properties(),
-                                                                new 
MockNotifiableTestStream() );
+                                                                new 
MockNotifiableTestStream(), log );
             forkStreamClient.consumeMultiLineContent( content.toString( ) );
             reporter = (MockReporter) forkStreamClient.getReporter( 
defaultChannel );
         }

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/6a79127a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/MockReporter.java
----------------------------------------------------------------------
diff --git 
a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/MockReporter.java
 
b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/MockReporter.java
index 3f2d221..b8c9ef0 100644
--- 
a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/MockReporter.java
+++ 
b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/MockReporter.java
@@ -22,7 +22,7 @@ package org.apache.maven.plugin.surefire.booterclient;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.atomic.AtomicInteger;
-import org.apache.maven.surefire.report.ConsoleLogger;
+import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
 import org.apache.maven.surefire.report.ConsoleOutputReceiver;
 import org.apache.maven.surefire.report.ReportEntry;
 import org.apache.maven.surefire.report.RunListener;
@@ -147,12 +147,38 @@ public class MockReporter
         testIgnored.incrementAndGet();
     }
 
+    public void debug( String message )
+    {
+        events.add( CONSOLE_OUTPUT );
+        data.add( message );
+    }
+
     public void info( String message )
     {
         events.add( CONSOLE_OUTPUT );
         data.add( message );
     }
 
+    public void warning( String message )
+    {
+        events.add( CONSOLE_OUTPUT );
+        data.add( message );
+    }
+
+    public void error( String message )
+    {
+        events.add( CONSOLE_OUTPUT );
+        data.add( message );
+    }
+
+    public void error( String message, Throwable t )
+    {
+    }
+
+    public void error( Throwable t )
+    {
+    }
+
     public void writeTestOutput( byte[] buf, int off, int len, boolean stdout )
     {
         events.add( stdout ? STDOUT : STDERR );

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/6a79127a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/TestSetMockReporterFactory.java
----------------------------------------------------------------------
diff --git 
a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/TestSetMockReporterFactory.java
 
b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/TestSetMockReporterFactory.java
index 2e337e2..ada54a2 100644
--- 
a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/TestSetMockReporterFactory.java
+++ 
b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/TestSetMockReporterFactory.java
@@ -19,10 +19,12 @@ package org.apache.maven.plugin.surefire.booterclient;
  * under the License.
  */
 
-import org.apache.maven.plugin.surefire.StartupReportConfiguration;
 import org.apache.maven.plugin.surefire.report.DefaultReporterFactory;
+import org.apache.maven.plugin.surefire.log.api.NullConsoleLogger;
 import org.apache.maven.surefire.report.RunListener;
 
+import static 
org.apache.maven.plugin.surefire.StartupReportConfiguration.defaultValue;
+
 /**
  * Internal tests use only.
  *
@@ -33,7 +35,7 @@ public class TestSetMockReporterFactory
 {
     public TestSetMockReporterFactory()
     {
-        super( StartupReportConfiguration.defaultValue() );
+        super( defaultValue(), new NullConsoleLogger() );
     }
 
     public RunListener createReporter()

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/6a79127a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/report/DefaultReporterFactoryTest.java
----------------------------------------------------------------------
diff --git 
a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/report/DefaultReporterFactoryTest.java
 
b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/report/DefaultReporterFactoryTest.java
index 090a11b..0a0a9c8 100644
--- 
a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/report/DefaultReporterFactoryTest.java
+++ 
b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/report/DefaultReporterFactoryTest.java
@@ -21,30 +21,31 @@ package org.apache.maven.plugin.surefire.report;
 
 import java.io.File;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.List;
 
 import junit.framework.TestCase;
 
 import org.apache.maven.plugin.surefire.StartupReportConfiguration;
-import org.apache.maven.surefire.report.DefaultDirectConsoleReporter;
+import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
+import org.apache.maven.plugin.surefire.log.api.NullConsoleLogger;
 import org.apache.maven.surefire.report.RunStatistics;
 import org.apache.maven.surefire.report.SafeThrowable;
 import org.apache.maven.surefire.report.StackTraceWriter;
 
+import static java.util.Arrays.asList;
 import static 
org.apache.maven.plugin.surefire.report.DefaultReporterFactory.TestResultType.error;
 import static 
org.apache.maven.plugin.surefire.report.DefaultReporterFactory.TestResultType.failure;
 import static 
org.apache.maven.plugin.surefire.report.DefaultReporterFactory.TestResultType.flake;
 import static 
org.apache.maven.plugin.surefire.report.DefaultReporterFactory.TestResultType.skipped;
 import static 
org.apache.maven.plugin.surefire.report.DefaultReporterFactory.TestResultType.success;
 import static 
org.apache.maven.plugin.surefire.report.DefaultReporterFactory.TestResultType.unknown;
+import static 
org.apache.maven.plugin.surefire.report.DefaultReporterFactory.getTestResultType;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
 public class DefaultReporterFactoryTest
     extends TestCase
 {
-
     private final static String TEST_ONE = "testOne";
 
     private final static String TEST_TWO = "testTwo";
@@ -61,10 +62,13 @@ public class DefaultReporterFactoryTest
 
     public void testMergeTestHistoryResult()
     {
-        StartupReportConfiguration reportConfig = new 
StartupReportConfiguration( true, true, "PLAIN", false, false, new 
File("target"), false, null, "TESTHASH",
-                                                                               
                  false, 1, null );
+        StartupReportConfiguration reportConfig =
+                new StartupReportConfiguration( true, true, "PLAIN", false, 
false, new File("target"), false, null,
+                                                      "TESTHASH", false, 1, 
null );
+
+        DummyTestReporter reporter = new DummyTestReporter();
 
-        DefaultReporterFactory factory = new DefaultReporterFactory( 
reportConfig );
+        DefaultReporterFactory factory = new DefaultReporterFactory( 
reportConfig, reporter );
 
         // First run, four tests failed and one passed
         List<TestMethodStats> firstRunStats = new ArrayList<TestMethodStats>();
@@ -114,88 +118,108 @@ public class DefaultReporterFactoryTest
         assertEquals( 0, mergedStatistics.getSkipped() );
 
         // Now test the result will be printed out correctly
-        DummyTestReporter reporter = new DummyTestReporter();
-        factory.printTestFailures( reporter, 
DefaultReporterFactory.TestResultType.flake );
+        factory.printTestFailures( flake );
         String[] expectedFlakeOutput =
             { "Flaked tests: ", TEST_FOUR, "  Run 1: " + ASSERTION_FAIL, "  
Run 2: PASS", "", TEST_ONE,
                 "  Run 1: " + ERROR, "  Run 2: " + ASSERTION_FAIL, "  Run 3: 
PASS", "", TEST_TWO, "  Run 1: " + ERROR,
                 "  Run 2: PASS", "" };
-        assertEquals( Arrays.asList( expectedFlakeOutput ), 
reporter.getMessages() );
+        assertEquals( asList( expectedFlakeOutput ), reporter.getMessages() );
 
-        reporter = new DummyTestReporter();
-        factory.printTestFailures( reporter, 
DefaultReporterFactory.TestResultType.error );
+        reporter.reset();
+        factory.printTestFailures( error );
         String[] expectedFailureOutput =
             { "Tests in error: ", TEST_THREE, "  Run 1: " + ASSERTION_FAIL, "  
Run 2: " + ERROR, "  Run 3: " + ERROR, ""
             };
-        assertEquals( Arrays.asList( expectedFailureOutput ), 
reporter.getMessages() );
+        assertEquals( asList( expectedFailureOutput ), reporter.getMessages() 
);
 
-        reporter = new DummyTestReporter();
-        factory.printTestFailures( reporter, 
DefaultReporterFactory.TestResultType.failure );
+        reporter.reset();
+        factory.printTestFailures( failure );
         String[] expectedErrorOutput = { };
-        assertEquals( Arrays.asList( expectedErrorOutput ), 
reporter.getMessages() );
+        assertEquals( asList( expectedErrorOutput ), reporter.getMessages() );
     }
 
-    static class DummyTestReporter
-        extends DefaultDirectConsoleReporter
+    static final class DummyTestReporter implements ConsoleLogger
     {
-
         private final List<String> messages = new ArrayList<String>();
 
-        public DummyTestReporter()
+        public void debug( String message )
+        {
+            messages.add( message );
+        }
+
+        public void info( String message )
+        {
+            messages.add( message );
+        }
+
+        public void warning( String message )
+        {
+            messages.add( message );
+        }
+
+        public void error( String message )
+        {
+            messages.add( message );
+        }
+
+        public void error( String message, Throwable t )
         {
-            super( System.out );
+            messages.add( message );
         }
 
-        @Override
-        public void info( String msg )
+        public void error( Throwable t )
         {
-            messages.add( msg );
         }
 
-        public List<String> getMessages()
+        List<String> getMessages()
         {
             return messages;
         }
+
+        void reset()
+        {
+            messages.clear();
+        }
     }
 
     public void testGetTestResultType()
     {
         List<ReportEntryType> emptyList = new ArrayList<ReportEntryType>();
-        assertEquals( unknown, DefaultReporterFactory.getTestResultType( 
emptyList, 1 ) );
+        assertEquals( unknown, getTestResultType( emptyList, 1 ) );
 
         List<ReportEntryType> successList = new ArrayList<ReportEntryType>();
         successList.add( ReportEntryType.SUCCESS );
         successList.add( ReportEntryType.SUCCESS );
-        assertEquals( success, DefaultReporterFactory.getTestResultType( 
successList, 1 ) );
+        assertEquals( success, getTestResultType( successList, 1 ) );
 
         List<ReportEntryType> failureErrorList = new 
ArrayList<ReportEntryType>();
         failureErrorList.add( ReportEntryType.FAILURE );
         failureErrorList.add( ReportEntryType.ERROR );
-        assertEquals( error, DefaultReporterFactory.getTestResultType( 
failureErrorList, 1 ) );
+        assertEquals( error, getTestResultType( failureErrorList, 1 ) );
 
         List<ReportEntryType> errorFailureList = new 
ArrayList<ReportEntryType>();
         errorFailureList.add( ReportEntryType.ERROR );
         errorFailureList.add( ReportEntryType.FAILURE );
-        assertEquals( error, DefaultReporterFactory.getTestResultType( 
errorFailureList, 1 ) );
+        assertEquals( error, getTestResultType( errorFailureList, 1 ) );
 
         List<ReportEntryType> flakeList = new ArrayList<ReportEntryType>();
         flakeList.add( ReportEntryType.SUCCESS );
         flakeList.add( ReportEntryType.FAILURE );
-        assertEquals( flake, DefaultReporterFactory.getTestResultType( 
flakeList, 1 ) );
+        assertEquals( flake, getTestResultType( flakeList, 1 ) );
 
-        assertEquals( failure, DefaultReporterFactory.getTestResultType( 
flakeList, 0 ) );
+        assertEquals( failure, getTestResultType( flakeList, 0 ) );
 
         flakeList = new ArrayList<ReportEntryType>();
         flakeList.add( ReportEntryType.ERROR );
         flakeList.add( ReportEntryType.SUCCESS );
         flakeList.add( ReportEntryType.FAILURE );
-        assertEquals( flake, DefaultReporterFactory.getTestResultType( 
flakeList, 1 ) );
+        assertEquals( flake, getTestResultType( flakeList, 1 ) );
 
-        assertEquals( error, DefaultReporterFactory.getTestResultType( 
flakeList, 0 ) );
+        assertEquals( error, getTestResultType( flakeList, 0 ) );
 
         List<ReportEntryType> skippedList = new ArrayList<ReportEntryType>();
         skippedList.add( ReportEntryType.SKIPPED );
-        assertEquals( skipped, DefaultReporterFactory.getTestResultType( 
skippedList, 1 ) );
+        assertEquals( skipped, getTestResultType( skippedList, 1 ) );
     }
 
     static class DummyStackTraceWriter

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/6a79127a/maven-surefire-common/src/test/java/org/apache/maven/surefire/JUnit4SuiteTest.java
----------------------------------------------------------------------
diff --git 
a/maven-surefire-common/src/test/java/org/apache/maven/surefire/JUnit4SuiteTest.java
 
b/maven-surefire-common/src/test/java/org/apache/maven/surefire/JUnit4SuiteTest.java
index 80cf5ff..d03f1b9 100644
--- 
a/maven-surefire-common/src/test/java/org/apache/maven/surefire/JUnit4SuiteTest.java
+++ 
b/maven-surefire-common/src/test/java/org/apache/maven/surefire/JUnit4SuiteTest.java
@@ -21,6 +21,7 @@ package org.apache.maven.surefire;
 
 import junit.framework.JUnit4TestAdapter;
 import junit.framework.Test;
+import org.apache.maven.plugin.surefire.SurefireReflectorTest;
 import org.apache.maven.plugin.surefire.SurefirePropertiesTest;
 import 
org.apache.maven.plugin.surefire.booterclient.BooterDeserializerProviderConfigurationTest;
 import 
org.apache.maven.plugin.surefire.booterclient.BooterDeserializerStartupConfigurationTest;
@@ -38,6 +39,7 @@ import 
org.apache.maven.plugin.surefire.util.SpecificFileFilterTest;
 import org.apache.maven.surefire.report.ConsoleOutputFileReporterTest;
 import org.apache.maven.surefire.report.FileReporterTest;
 import org.apache.maven.surefire.report.RunStatisticsTest;
+import org.apache.maven.surefire.spi.SPITest;
 import org.apache.maven.surefire.util.RelocatorTest;
 import org.junit.runner.RunWith;
 import org.junit.runners.Suite;
@@ -66,7 +68,9 @@ import org.junit.runners.Suite;
     BooterDeserializerStartupConfigurationTest.class,
     BooterDeserializerProviderConfigurationTest.class,
     TestProvidingInputStreamTest.class,
-    TestLessInputStreamBuilderTest.class
+    TestLessInputStreamBuilderTest.class,
+    SPITest.class,
+    SurefireReflectorTest.class
 } )
 @RunWith( Suite.class )
 public class JUnit4SuiteTest

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/6a79127a/maven-surefire-common/src/test/java/org/apache/maven/surefire/spi/CustomizedImpl.java
----------------------------------------------------------------------
diff --git 
a/maven-surefire-common/src/test/java/org/apache/maven/surefire/spi/CustomizedImpl.java
 
b/maven-surefire-common/src/test/java/org/apache/maven/surefire/spi/CustomizedImpl.java
new file mode 100644
index 0000000..70e2255
--- /dev/null
+++ 
b/maven-surefire-common/src/test/java/org/apache/maven/surefire/spi/CustomizedImpl.java
@@ -0,0 +1,33 @@
+package org.apache.maven.surefire.spi;
+
+/*
+ * 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.
+ */
+
+/**
+ * @author <a href="mailto:tibordig...@apache.org";>Tibor Digana (tibor17)</a>
+ * @since 2.19.2
+ */
+public class CustomizedImpl
+    implements IDefault
+{
+    public boolean isDefault()
+    {
+        return false;
+    }
+}

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/6a79127a/maven-surefire-common/src/test/java/org/apache/maven/surefire/spi/DefaultImpl.java
----------------------------------------------------------------------
diff --git 
a/maven-surefire-common/src/test/java/org/apache/maven/surefire/spi/DefaultImpl.java
 
b/maven-surefire-common/src/test/java/org/apache/maven/surefire/spi/DefaultImpl.java
new file mode 100644
index 0000000..e623f0b
--- /dev/null
+++ 
b/maven-surefire-common/src/test/java/org/apache/maven/surefire/spi/DefaultImpl.java
@@ -0,0 +1,33 @@
+package org.apache.maven.surefire.spi;
+
+/*
+ * 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.
+ */
+
+/**
+ * @author <a href="mailto:tibordig...@apache.org";>Tibor Digana (tibor17)</a>
+ * @since 2.19.2
+ */
+public class DefaultImpl
+    implements IDefault
+{
+    public boolean isDefault()
+    {
+        return true;
+    }
+}

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/6a79127a/maven-surefire-common/src/test/java/org/apache/maven/surefire/spi/EmptyServiceInterface.java
----------------------------------------------------------------------
diff --git 
a/maven-surefire-common/src/test/java/org/apache/maven/surefire/spi/EmptyServiceInterface.java
 
b/maven-surefire-common/src/test/java/org/apache/maven/surefire/spi/EmptyServiceInterface.java
new file mode 100644
index 0000000..38a186c
--- /dev/null
+++ 
b/maven-surefire-common/src/test/java/org/apache/maven/surefire/spi/EmptyServiceInterface.java
@@ -0,0 +1,30 @@
+package org.apache.maven.surefire.spi;
+
+/*
+ * 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.
+ */
+
+
+/**
+ * @author <a href="mailto:tibordig...@apache.org";>Tibor Digana (tibor17)</a>
+ * @since 2.19.2
+ */
+public interface EmptyServiceInterface
+{
+    String whoAmI();
+}

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/6a79127a/maven-surefire-common/src/test/java/org/apache/maven/surefire/spi/ExistingServiceInterface.java
----------------------------------------------------------------------
diff --git 
a/maven-surefire-common/src/test/java/org/apache/maven/surefire/spi/ExistingServiceInterface.java
 
b/maven-surefire-common/src/test/java/org/apache/maven/surefire/spi/ExistingServiceInterface.java
new file mode 100644
index 0000000..9d7fa25
--- /dev/null
+++ 
b/maven-surefire-common/src/test/java/org/apache/maven/surefire/spi/ExistingServiceInterface.java
@@ -0,0 +1,30 @@
+package org.apache.maven.surefire.spi;
+
+/*
+ * 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.
+ */
+
+
+/**
+ * @author <a href="mailto:tibordig...@apache.org";>Tibor Digana (tibor17)</a>
+ * @since 2.19.2
+ */
+public interface ExistingServiceInterface
+{
+    String whoAmI();
+}

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/6a79127a/maven-surefire-common/src/test/java/org/apache/maven/surefire/spi/IDefault.java
----------------------------------------------------------------------
diff --git 
a/maven-surefire-common/src/test/java/org/apache/maven/surefire/spi/IDefault.java
 
b/maven-surefire-common/src/test/java/org/apache/maven/surefire/spi/IDefault.java
new file mode 100644
index 0000000..53b7b38
--- /dev/null
+++ 
b/maven-surefire-common/src/test/java/org/apache/maven/surefire/spi/IDefault.java
@@ -0,0 +1,33 @@
+package org.apache.maven.surefire.spi;
+
+/*
+ * 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.
+ */
+
+/**
+ * @author <a href="mailto:tibordig...@apache.org";>Tibor Digana (tibor17)</a>
+ * @since 2.19.2
+ */
+public interface IDefault
+{
+    /**
+     * @return <tt>true</tt> if SPI implementation vendor is 
maven-surefire-plugin or maven-failsafe-plugin.
+     * <tt>false</tt> if customized by users.
+     */
+    boolean isDefault();
+}

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/6a79127a/maven-surefire-common/src/test/java/org/apache/maven/surefire/spi/NoServiceInterface.java
----------------------------------------------------------------------
diff --git 
a/maven-surefire-common/src/test/java/org/apache/maven/surefire/spi/NoServiceInterface.java
 
b/maven-surefire-common/src/test/java/org/apache/maven/surefire/spi/NoServiceInterface.java
new file mode 100644
index 0000000..af649b9
--- /dev/null
+++ 
b/maven-surefire-common/src/test/java/org/apache/maven/surefire/spi/NoServiceInterface.java
@@ -0,0 +1,30 @@
+package org.apache.maven.surefire.spi;
+
+/*
+ * 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.
+ */
+
+
+/**
+ * @author <a href="mailto:tibordig...@apache.org";>Tibor Digana (tibor17)</a>
+ * @since 2.19.2
+ */
+public interface NoServiceInterface
+{
+    void dummyService();
+}

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/6a79127a/maven-surefire-common/src/test/java/org/apache/maven/surefire/spi/SPITest.java
----------------------------------------------------------------------
diff --git 
a/maven-surefire-common/src/test/java/org/apache/maven/surefire/spi/SPITest.java
 
b/maven-surefire-common/src/test/java/org/apache/maven/surefire/spi/SPITest.java
new file mode 100644
index 0000000..a8058f8
--- /dev/null
+++ 
b/maven-surefire-common/src/test/java/org/apache/maven/surefire/spi/SPITest.java
@@ -0,0 +1,89 @@
+package org.apache.maven.surefire.spi;
+
+/*
+ * 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.plugin.surefire.booterclient.ProviderDetector;
+import org.junit.Test;
+
+import java.io.IOException;
+
+import static java.lang.Thread.currentThread;
+import static org.fest.assertions.Assertions.assertThat;
+
+/**
+ * @author <a href="mailto:tibordig...@apache.org";>Tibor Digana (tibor17)</a>
+ * @since 2.19.2
+ */
+public class SPITest
+{
+    private final ServiceLoader spi = new ServiceLoader();
+    private final ProviderDetector providerDetector = new ProviderDetector();
+    private final ClassLoader ctx = currentThread().getContextClassLoader();
+
+    @Test
+    public void shouldNotLoadSpiDoesNotExist() throws IOException
+    {
+        assertThat( spi.lookup( NoServiceInterface.class, ctx ) )
+                .isEmpty();
+
+        assertThat( spi.load( NoServiceInterface.class, ctx ) )
+                .isEmpty();
+
+        assertThat( providerDetector.lookupServiceNames( 
NoServiceInterface.class, ctx ) )
+                .isEmpty();
+    }
+
+    @Test
+    public void shouldNotLoadEmptySpi() throws IOException
+    {
+        assertThat( spi.lookup( EmptyServiceInterface.class, ctx ) )
+                .isEmpty();
+
+        assertThat( spi.load( EmptyServiceInterface.class, ctx ) )
+                .isEmpty();
+
+        assertThat( providerDetector.lookupServiceNames( 
EmptyServiceInterface.class, ctx ) )
+                .isEmpty();
+    }
+
+    @Test
+    public void shouldLoad2SpiObjects() throws IOException
+    {
+        assertThat( spi.lookup( ExistingServiceInterface.class, ctx ) )
+                .hasSize( 2 );
+
+        assertThat( spi.lookup( ExistingServiceInterface.class, ctx ) )
+                .containsOnly( SPImpl1.class.getName(), 
SPImpl2.class.getName() );
+
+
+        assertThat( spi.load( ExistingServiceInterface.class, ctx ) )
+                .hasSize( 2 );
+
+        assertThat( spi.load( ExistingServiceInterface.class, ctx ) )
+                .contains( new SPImpl1(), new SPImpl2() );
+
+
+        assertThat( providerDetector.lookupServiceNames( 
ExistingServiceInterface.class, ctx ) )
+                .hasSize( 2 );
+
+        assertThat( providerDetector.lookupServiceNames( 
ExistingServiceInterface.class, ctx ) )
+                .containsOnly( SPImpl1.class.getName(), 
SPImpl2.class.getName() );
+    }
+}

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/6a79127a/maven-surefire-common/src/test/java/org/apache/maven/surefire/spi/SPImpl1.java
----------------------------------------------------------------------
diff --git 
a/maven-surefire-common/src/test/java/org/apache/maven/surefire/spi/SPImpl1.java
 
b/maven-surefire-common/src/test/java/org/apache/maven/surefire/spi/SPImpl1.java
new file mode 100644
index 0000000..e96cec7
--- /dev/null
+++ 
b/maven-surefire-common/src/test/java/org/apache/maven/surefire/spi/SPImpl1.java
@@ -0,0 +1,44 @@
+package org.apache.maven.surefire.spi;
+
+/*
+ * 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.
+ */
+
+/**
+ * @author <a href="mailto:tibordig...@apache.org";>Tibor Digana (tibor17)</a>
+ * @since 2.19.2
+ */
+public class SPImpl1 implements ExistingServiceInterface
+{
+    public String whoAmI()
+    {
+        return SPImpl1.class.getSimpleName();
+    }
+
+    @Override
+    public boolean equals( Object o )
+    {
+        return this == o || getClass() == o.getClass();
+    }
+
+    @Override
+    public int hashCode()
+    {
+        return whoAmI().hashCode();
+    }
+}

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/6a79127a/maven-surefire-common/src/test/java/org/apache/maven/surefire/spi/SPImpl2.java
----------------------------------------------------------------------
diff --git 
a/maven-surefire-common/src/test/java/org/apache/maven/surefire/spi/SPImpl2.java
 
b/maven-surefire-common/src/test/java/org/apache/maven/surefire/spi/SPImpl2.java
new file mode 100644
index 0000000..75806b9
--- /dev/null
+++ 
b/maven-surefire-common/src/test/java/org/apache/maven/surefire/spi/SPImpl2.java
@@ -0,0 +1,44 @@
+package org.apache.maven.surefire.spi;
+
+/*
+ * 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.
+ */
+
+/**
+ * @author <a href="mailto:tibordig...@apache.org";>Tibor Digana (tibor17)</a>
+ * @since 2.19.2
+ */
+public class SPImpl2 implements ExistingServiceInterface
+{
+    public String whoAmI()
+    {
+        return SPImpl2.class.getSimpleName();
+    }
+
+    @Override
+    public boolean equals( Object o )
+    {
+        return this == o || getClass() == o.getClass();
+    }
+
+    @Override
+    public int hashCode()
+    {
+        return whoAmI().hashCode();
+    }
+}

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/6a79127a/maven-surefire-common/src/test/resources/META-INF/services/org.apache.maven.surefire.spi.EmptyServiceInterface
----------------------------------------------------------------------
diff --git 
a/maven-surefire-common/src/test/resources/META-INF/services/org.apache.maven.surefire.spi.EmptyServiceInterface
 
b/maven-surefire-common/src/test/resources/META-INF/services/org.apache.maven.surefire.spi.EmptyServiceInterface
new file mode 100644
index 0000000..42d690d
--- /dev/null
+++ 
b/maven-surefire-common/src/test/resources/META-INF/services/org.apache.maven.surefire.spi.EmptyServiceInterface
@@ -0,0 +1,21 @@
+#
+# 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.
+#
+
+# this is just a comment and should not be retrieved from SPI loader
+# here should not be any class specified!

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/6a79127a/maven-surefire-common/src/test/resources/META-INF/services/org.apache.maven.surefire.spi.ExistingServiceInterface
----------------------------------------------------------------------
diff --git 
a/maven-surefire-common/src/test/resources/META-INF/services/org.apache.maven.surefire.spi.ExistingServiceInterface
 
b/maven-surefire-common/src/test/resources/META-INF/services/org.apache.maven.surefire.spi.ExistingServiceInterface
new file mode 100644
index 0000000..9d34f7f
--- /dev/null
+++ 
b/maven-surefire-common/src/test/resources/META-INF/services/org.apache.maven.surefire.spi.ExistingServiceInterface
@@ -0,0 +1,24 @@
+#
+# 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.
+#
+
+# this is just a comment and should not be retrieved from SPI loader
+org.apache.maven.surefire.spi.SPImpl1
+
+# another comment
+org.apache.maven.surefire.spi.SPImpl2

Reply via email to