Repository: maven-surefire
Updated Branches:
  refs/heads/master 2b4629c71 -> c26bc4b9e


[SUREFIRE-1137] Ensure that all communication with the forked process is 
encoded with a fixed 8-bit charset, to fix sysout/syserr encoding in case the 
fork ends up with a different default charset than the main process


Project: http://git-wip-us.apache.org/repos/asf/maven-surefire/repo
Commit: http://git-wip-us.apache.org/repos/asf/maven-surefire/commit/c26bc4b9
Tree: http://git-wip-us.apache.org/repos/asf/maven-surefire/tree/c26bc4b9
Diff: http://git-wip-us.apache.org/repos/asf/maven-surefire/diff/c26bc4b9

Branch: refs/heads/master
Commit: c26bc4b9e60de515e7e730207ebf0aacb372dd04
Parents: 2b4629c
Author: Andreas Gudian <agud...@apache.org>
Authored: Tue Feb 3 22:30:08 2015 +0100
Committer: Andreas Gudian <agud...@apache.org>
Committed: Thu Feb 5 20:54:02 2015 +0100

----------------------------------------------------------------------
 .../surefire/booterclient/ForkStarter.java      |  5 +-
 .../TestProvidingInputStream.java               |  4 +-
 .../booterclient/output/ForkClient.java         | 33 +++++++++--
 .../surefire/report/DirectConsoleOutput.java    | 16 ++++-
 .../booterclient/ForkingRunListenerTest.java    |  6 +-
 pom.xml                                         |  2 +-
 .../surefire/booter/ForkingRunListener.java     | 62 ++++++++------------
 .../surefire/report/ConsoleOutputCapture.java   |  8 +--
 .../surefire/util/internal/StringUtils.java     | 57 ++++++++++++++----
 .../surefire/util/internal/StringUtilsTest.java |  9 +--
 .../maven/surefire/booter/ForkedBooter.java     | 18 ++++--
 .../maven/surefire/booter/LazyTestsToRun.java   | 17 +++++-
 .../maven/surefire/its/ConsoleOutputIT.java     | 47 +++++++++++++--
 .../maven/surefire/its/fixture/TestFile.java    |  2 +-
 .../src/test/resources/consoleOutput/pom.xml    | 17 ++----
 .../src/test/java/consoleOutput/Test1.java      | 17 +++---
 16 files changed, 220 insertions(+), 100 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/c26bc4b9/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ForkStarter.java
----------------------------------------------------------------------
diff --git 
a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ForkStarter.java
 
b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ForkStarter.java
index 12b224a..b1755ba 100644
--- 
a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ForkStarter.java
+++ 
b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ForkStarter.java
@@ -22,6 +22,7 @@ package org.apache.maven.plugin.surefire.booterclient;
 import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
+import java.nio.charset.Charset;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Iterator;
@@ -68,6 +69,7 @@ import org.apache.maven.surefire.report.StackTraceWriter;
 import org.apache.maven.surefire.suite.RunResult;
 import org.apache.maven.surefire.testset.TestRequest;
 import org.apache.maven.surefire.util.DefaultScanResult;
+import org.apache.maven.surefire.util.internal.StringUtils;
 
 import static org.apache.maven.surefire.booter.Classpath.join;
 
@@ -468,7 +470,8 @@ public class ForkStarter
             final int timeout = forkedProcessTimeoutInSeconds > 0 ? 
forkedProcessTimeoutInSeconds : 0;
             final int result =
                 CommandLineUtils.executeCommandLine( cli, 
testProvidingInputStream, threadedStreamConsumer,
-                                                     threadedStreamConsumer, 
timeout, inputStreamCloser );
+                                                     threadedStreamConsumer, 
timeout, inputStreamCloser,
+                                                     Charset.forName( 
StringUtils.FORK_STREAM_CHARSET_NAME ) );
             if ( result != RunResult.SUCCESS )
             {
                 throw new SurefireBooterForkException( "Error occurred in 
starting fork, check output in log" );

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/c26bc4b9/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/TestProvidingInputStream.java
----------------------------------------------------------------------
diff --git 
a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/TestProvidingInputStream.java
 
b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/TestProvidingInputStream.java
index a33ce62..e7a5032 100644
--- 
a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/TestProvidingInputStream.java
+++ 
b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/TestProvidingInputStream.java
@@ -24,6 +24,8 @@ import java.io.InputStream;
 import java.util.Queue;
 import java.util.concurrent.Semaphore;
 
+import static 
org.apache.maven.surefire.util.internal.StringUtils.encodeStringForForkCommunication;
+
 /**
  * An {@link InputStream} that, when read, provides test class names out of a 
queue.
  * <p/>
@@ -90,7 +92,7 @@ public class TestProvidingInputStream
             String currentElement = testItemQueue.poll();
             if ( currentElement != null )
             {
-                currentBuffer = currentElement.getBytes();
+                currentBuffer = encodeStringForForkCommunication( 
currentElement );
                 currentPos = 0;
             }
             else

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/c26bc4b9/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/ForkClient.java
----------------------------------------------------------------------
diff --git 
a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/ForkClient.java
 
b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/ForkClient.java
index 7a2a15b..b6bbd55 100644
--- 
a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/ForkClient.java
+++ 
b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/ForkClient.java
@@ -22,6 +22,7 @@ package org.apache.maven.plugin.surefire.booterclient.output;
 import java.io.BufferedReader;
 import java.io.IOException;
 import java.io.StringReader;
+import java.nio.ByteBuffer;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
@@ -140,14 +141,10 @@ public class ForkClient
                     }
                     break;
                 case ForkingRunListener.BOOTERCODE_STDOUT:
-                    byte[] bytes = new byte[remaining.length()];
-                    int len = StringUtils.unescapeBytes( bytes, remaining );
-                    getOrCreateConsoleOutputReceiver( channelNumber 
).writeTestOutput( bytes, 0, len, true );
+                    writeTestOutput( channelNumber, remaining, true );
                     break;
                 case ForkingRunListener.BOOTERCODE_STDERR:
-                    bytes = new byte[remaining.length()];
-                    len = StringUtils.unescapeBytes( bytes, remaining );
-                    getOrCreateConsoleOutputReceiver( channelNumber 
).writeTestOutput( bytes, 0, len, false );
+                    writeTestOutput( channelNumber, remaining, false );
                     break;
                 case ForkingRunListener.BOOTERCODE_CONSOLE:
                     getOrCreateConsoleLogger( channelNumber ).info( 
createConsoleMessage( remaining ) );
@@ -184,6 +181,30 @@ public class ForkClient
         }
     }
 
+    private void writeTestOutput( final Integer channelNumber, final String 
remaining, boolean isStdout )
+    {
+        int csNameEnd = remaining.indexOf( ',' );
+        String charsetName = remaining.substring( 0, csNameEnd );
+        String byteEncoded = remaining.substring( csNameEnd + 1 );
+        ByteBuffer unescaped = StringUtils.unescapeBytes( byteEncoded, 
charsetName );
+
+        if ( unescaped.hasArray() )
+        {
+            byte[] convertedBytes = unescaped.array();
+
+            getOrCreateConsoleOutputReceiver( channelNumber )
+                .writeTestOutput( convertedBytes, unescaped.position(), 
unescaped.remaining(), isStdout );
+        }
+        else
+        {
+            byte[] convertedBytes = new byte[unescaped.remaining()];
+            unescaped.get( convertedBytes, 0, unescaped.remaining() );
+
+            getOrCreateConsoleOutputReceiver( channelNumber )
+                .writeTestOutput( convertedBytes, 0, convertedBytes.length, 
isStdout );
+        }
+    }
+
     public void consumeMultiLineContent( String s )
         throws IOException
     {

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/c26bc4b9/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 ab710e9..bd5ba7c 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
@@ -20,6 +20,11 @@ package org.apache.maven.plugin.surefire.report;
  */
 
 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;
 
 /**
@@ -47,7 +52,16 @@ public class DirectConsoleOutput
     public void writeTestOutput( byte[] buf, int off, int len, boolean stdout )
     {
         PrintStream stream = stdout ? sout : serr;
-        stream.write( buf, off, len );
+
+        try
+        {
+            CharBuffer decode = Charset.defaultCharset().newDecoder().decode( 
ByteBuffer.wrap( buf, off, len ) );
+            stream.append( decode );
+        }
+        catch ( CharacterCodingException e )
+        {
+            stream.write( buf, off, len );
+        }
     }
 
     public void testSetStarting( ReportEntry reportEntry )

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/c26bc4b9/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 ca64092..fd85b44 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
@@ -22,9 +22,11 @@ package org.apache.maven.plugin.surefire.booterclient;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.PrintStream;
+import java.nio.charset.Charset;
 import java.util.List;
 import java.util.Properties;
 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;
@@ -72,14 +74,14 @@ public class ForkingRunListenerTest
     {
         final byte[] header = ForkingRunListener.createHeader( (byte) 'F', 
0xCAFE );
         String asString = new String( header );
-        assertEquals( "F,cafe,", asString );
+        assertEquals( "F,cafe," + Charset.defaultCharset().name() + ",", 
asString );
     }
 
     public void testHeaderCreationShort()
     {
         final byte[] header = ForkingRunListener.createHeader( (byte) 'F', 0xE 
);
         String asString = new String( header );
-        assertEquals( "F,000e,", asString );
+        assertEquals( "F,e," + Charset.defaultCharset().name() + ",", asString 
);
     }
 
     public void testSetStarting()

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/c26bc4b9/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 1d6087c..68f270e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -206,7 +206,7 @@
       <dependency>
         <groupId>org.apache.maven.shared</groupId>
         <artifactId>maven-shared-utils</artifactId>
-        <version>0.4</version>
+        <version>0.8-SNAPSHOT</version>
       </dependency>
       <dependency>
         <groupId>org.apache.maven.shared</groupId>

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/c26bc4b9/surefire-api/src/main/java/org/apache/maven/surefire/booter/ForkingRunListener.java
----------------------------------------------------------------------
diff --git 
a/surefire-api/src/main/java/org/apache/maven/surefire/booter/ForkingRunListener.java
 
b/surefire-api/src/main/java/org/apache/maven/surefire/booter/ForkingRunListener.java
index 5874d0b..77af0d5 100644
--- 
a/surefire-api/src/main/java/org/apache/maven/surefire/booter/ForkingRunListener.java
+++ 
b/surefire-api/src/main/java/org/apache/maven/surefire/booter/ForkingRunListener.java
@@ -20,6 +20,7 @@ package org.apache.maven.surefire.booter;
  */
 
 import java.io.PrintStream;
+import java.nio.charset.Charset;
 import java.util.Enumeration;
 import java.util.Properties;
 
@@ -31,6 +32,8 @@ import org.apache.maven.surefire.report.SafeThrowable;
 import org.apache.maven.surefire.report.StackTraceWriter;
 import org.apache.maven.surefire.util.internal.StringUtils;
 
+import static 
org.apache.maven.surefire.util.internal.StringUtils.encodeStringForForkCommunication;
+
 /**
  * Encodes the full output of the test run to the stdout stream.
  * <p/>
@@ -81,7 +84,6 @@ public class ForkingRunListener
 
     public static final byte BOOTERCODE_BYE = (byte) 'Z';
 
-
     private final PrintStream target;
 
     private final int testSetChannelId;
@@ -104,42 +106,42 @@ public class ForkingRunListener
 
     public void testSetStarting( ReportEntry report )
     {
-        target.print( toString( BOOTERCODE_TESTSET_STARTING, report, 
testSetChannelId ) );
+        encodeAndWriteToTarget( toString( BOOTERCODE_TESTSET_STARTING, report, 
testSetChannelId ) );
     }
 
     public void testSetCompleted( ReportEntry report )
     {
-        target.print( toString( BOOTERCODE_TESTSET_COMPLETED, report, 
testSetChannelId ) );
+        encodeAndWriteToTarget( toString( BOOTERCODE_TESTSET_COMPLETED, 
report, testSetChannelId ) );
     }
 
     public void testStarting( ReportEntry report )
     {
-        target.print( toString( BOOTERCODE_TEST_STARTING, report, 
testSetChannelId ) );
+        encodeAndWriteToTarget( toString( BOOTERCODE_TEST_STARTING, report, 
testSetChannelId ) );
     }
 
     public void testSucceeded( ReportEntry report )
     {
-        target.print( toString( BOOTERCODE_TEST_SUCCEEDED, report, 
testSetChannelId ) );
+        encodeAndWriteToTarget( toString( BOOTERCODE_TEST_SUCCEEDED, report, 
testSetChannelId ) );
     }
 
     public void testAssumptionFailure( ReportEntry report )
     {
-        target.print( toString( BOOTERCODE_TEST_ASSUMPTIONFAILURE, report, 
testSetChannelId ) );
+        encodeAndWriteToTarget( toString( BOOTERCODE_TEST_ASSUMPTIONFAILURE, 
report, testSetChannelId ) );
     }
 
     public void testError( ReportEntry report )
     {
-        target.print( toString( BOOTERCODE_TEST_ERROR, report, 
testSetChannelId ) );
+        encodeAndWriteToTarget( toString( BOOTERCODE_TEST_ERROR, report, 
testSetChannelId ) );
     }
 
     public void testFailed( ReportEntry report )
     {
-        target.print( toString( BOOTERCODE_TEST_FAILED, report, 
testSetChannelId ) );
+        encodeAndWriteToTarget( toString( BOOTERCODE_TEST_FAILED, report, 
testSetChannelId ) );
     }
 
     public void testSkipped( ReportEntry report )
     {
-        target.print( toString( BOOTERCODE_TEST_SKIPPED, report, 
testSetChannelId ) );
+        encodeAndWriteToTarget( toString( BOOTERCODE_TEST_SKIPPED, report, 
testSetChannelId ) );
     }
 
     void sendProps()
@@ -148,7 +150,7 @@ public class ForkingRunListener
 
         if ( systemProperties != null )
         {
-            Enumeration propertyKeys = systemProperties.propertyNames();
+            Enumeration<?> propertyKeys = systemProperties.propertyNames();
 
             while ( propertyKeys.hasMoreElements() )
             {
@@ -160,7 +162,7 @@ public class ForkingRunListener
                 {
                     value = "null";
                 }
-                target.print( toPropertyString( key, value ) );
+                encodeAndWriteToTarget( toPropertyString( key, value ) );
             }
         }
     }
@@ -182,34 +184,14 @@ public class ForkingRunListener
 
     public static byte[] createHeader( byte booterCode, int testSetChannel )
     {
-        byte[] header = new byte[7];
-        header[0] = booterCode;
-        header[1] = (byte) ',';
-        header[6] = (byte) ',';
-
-        int i = testSetChannel;
-        int charPos = 6;
-        int radix = 1 << 4;
-        int mask = radix - 1;
-        do
-        {
-            header[--charPos] = (byte) DIGITS[i & mask];
-            i >>>= 4;
-        }
-        while ( i != 0 );
+        StringBuilder sb = new StringBuilder();
+        sb.append( (char) booterCode ).append( ',' );
+        sb.append( Integer.toString( testSetChannel, 16 ) ).append( ',' );
+        sb.append( Charset.defaultCharset().name() ).append( ',' );
 
-        while ( charPos > 2 )
-        {
-            header[--charPos] = (byte) '0';
-        }
-        return header;
+        return encodeStringForForkCommunication( sb.toString() );
     }
 
-    private static final char[] DIGITS =
-        { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 
'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
-            'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 
'z' };
-
-
     public void info( String message )
     {
         if ( message != null )
@@ -220,10 +202,16 @@ public class ForkingRunListener
             StringUtils.escapeToPrintable( sb, message );
 
             sb.append( '\n' );
-            target.print( sb.toString() );
+            encodeAndWriteToTarget( sb.toString() );
         }
     }
 
+    private void encodeAndWriteToTarget( String string )
+    {
+        byte[] encodeBytes = encodeStringForForkCommunication( string );
+        target.write( encodeBytes, 0, encodeBytes.length );
+    }
+
     private String toPropertyString( String key, String value )
     {
         StringBuilder stringBuilder = new StringBuilder();

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/c26bc4b9/surefire-api/src/main/java/org/apache/maven/surefire/report/ConsoleOutputCapture.java
----------------------------------------------------------------------
diff --git 
a/surefire-api/src/main/java/org/apache/maven/surefire/report/ConsoleOutputCapture.java
 
b/surefire-api/src/main/java/org/apache/maven/surefire/report/ConsoleOutputCapture.java
index 7421703..27b0ee2 100644
--- 
a/surefire-api/src/main/java/org/apache/maven/surefire/report/ConsoleOutputCapture.java
+++ 
b/surefire-api/src/main/java/org/apache/maven/surefire/report/ConsoleOutputCapture.java
@@ -22,7 +22,6 @@ package org.apache.maven.surefire.report;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.PrintStream;
-import org.apache.maven.surefire.util.internal.ByteBuffer;
 
 /**
  * Deals with system.out/err.
@@ -78,17 +77,14 @@ public class ConsoleOutputCapture
             }
         }
 
-        static final byte[] NL = new byte[]{ (byte) '\n' };
-
         public void println( String s )
         {
             if ( s == null )
             {
                 s = "null"; // Shamelessly taken from super.print
             }
-            final byte[] bytes = s.getBytes();
-            final byte[] join = ByteBuffer.join( bytes, 0, bytes.length, NL, 
0, 1 );
-            target.writeTestOutput( join, 0, join.length, isStdout );
+            final byte[] bytes = ( s + "\n" ).getBytes();
+            target.writeTestOutput( bytes, 0, bytes.length, isStdout );
         }
 
         public void close()

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/c26bc4b9/surefire-api/src/main/java/org/apache/maven/surefire/util/internal/StringUtils.java
----------------------------------------------------------------------
diff --git 
a/surefire-api/src/main/java/org/apache/maven/surefire/util/internal/StringUtils.java
 
b/surefire-api/src/main/java/org/apache/maven/surefire/util/internal/StringUtils.java
index f9137cf..3f29f8a 100644
--- 
a/surefire-api/src/main/java/org/apache/maven/surefire/util/internal/StringUtils.java
+++ 
b/surefire-api/src/main/java/org/apache/maven/surefire/util/internal/StringUtils.java
@@ -19,6 +19,11 @@ package org.apache.maven.surefire.util.internal;
  * under the License.
  */
 
+import java.io.UnsupportedEncodingException;
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.charset.CharacterCodingException;
+import java.nio.charset.Charset;
 import java.util.StringTokenizer;
 
 /**
@@ -56,6 +61,11 @@ public class StringUtils
         '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
         'A', 'B', 'C', 'D', 'E', 'F' };
 
+    private static final Charset DEFAULT_CHARSET = Charset.defaultCharset();
+
+    // 8-bit charset Latin-1
+    public static final String FORK_STREAM_CHARSET_NAME = "ISO-8859-1";
+
     public static String[] split( String text, String separator )
     {
         int max = -1;
@@ -264,24 +274,21 @@ public class StringUtils
 
     /**
      * Reverses the effect of {@link #escapeBytesToPrintable(byte[], int, 
byte[], int, int)}.
-     * <p>
-     * A save length of {@code out} is {@code str.length()}
      *
-     * @param out the target byte array
      * @param str the input String
+     * @param charsetName the charset name
      * @return the number of bytes written to {@code out}
      */
-    public static int unescapeBytes( byte[] out, String str )
+    public static ByteBuffer unescapeBytes( String str, String charsetName  )
     {
         int outPos = 0;
-        if ( out == null )
-        {
-            throw new IllegalArgumentException( "The output array must not be 
null" );
-        }
+
         if ( str == null )
         {
-            return 0;
+            return ByteBuffer.wrap( new byte[0] );
         }
+
+        byte[] out = new byte[str.length()];
         for ( int i = 0; i < str.length(); i++ )
         {
             char ch = str.charAt( i );
@@ -297,6 +304,36 @@ public class StringUtils
                 out[outPos++] = (byte) ch;
             }
         }
-        return outPos;
+
+        Charset sourceCharset = Charset.forName( charsetName );
+        if ( !DEFAULT_CHARSET.equals( sourceCharset ) )
+        {
+            CharBuffer decodedFromSourceCharset;
+            try
+            {
+                decodedFromSourceCharset = sourceCharset.newDecoder().decode( 
ByteBuffer.wrap( out, 0, outPos ) );
+                ByteBuffer defaultEncoded = DEFAULT_CHARSET.encode( 
decodedFromSourceCharset );
+
+                return defaultEncoded;
+            }
+            catch ( CharacterCodingException e )
+            {
+                // ignore and fall through to the non-recoded version
+            }
+        }
+
+        return ByteBuffer.wrap( out, 0, outPos );
+    }
+
+    public static byte[] encodeStringForForkCommunication( String string )
+    {
+        try
+        {
+            return string.getBytes( FORK_STREAM_CHARSET_NAME );
+        }
+        catch ( UnsupportedEncodingException e )
+        {
+           throw new RuntimeException( "The JVM must support Charset " + 
FORK_STREAM_CHARSET_NAME, e );
+        }
     }
 }

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/c26bc4b9/surefire-api/src/test/java/org/apache/maven/surefire/util/internal/StringUtilsTest.java
----------------------------------------------------------------------
diff --git 
a/surefire-api/src/test/java/org/apache/maven/surefire/util/internal/StringUtilsTest.java
 
b/surefire-api/src/test/java/org/apache/maven/surefire/util/internal/StringUtilsTest.java
index 89a8c50..c1c8d73 100644
--- 
a/surefire-api/src/test/java/org/apache/maven/surefire/util/internal/StringUtilsTest.java
+++ 
b/surefire-api/src/test/java/org/apache/maven/surefire/util/internal/StringUtilsTest.java
@@ -19,6 +19,8 @@ package org.apache.maven.surefire.util.internal;
  * under the License.
  */
 
+import java.nio.charset.Charset;
+
 import junit.framework.TestCase;
 
 /**
@@ -82,14 +84,13 @@ public class StringUtilsTest
 
         assertEquals( escapedBytes, escapedString.length() );
 
-        byte[] unescaped = new byte[input.length];
-        int unescapeBytes = StringUtils.unescapeBytes( unescaped, 
escapedString );
+        java.nio.ByteBuffer unescaped = StringUtils.unescapeBytes( 
escapedString, Charset.defaultCharset().name() );
 
-        assertEquals( input.length, unescapeBytes );
+        assertEquals( input.length, unescaped.remaining() - 
unescaped.position() );
 
         for ( int i = 0; i < input.length; i++ )
         {
-            assertEquals( "At position " + i, input[i], unescaped[i] );
+            assertEquals( "At position " + i, input[i], unescaped.get() );
         }
     }
 }

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/c26bc4b9/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ForkedBooter.java
----------------------------------------------------------------------
diff --git 
a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ForkedBooter.java
 
b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ForkedBooter.java
index fcd44d8..2f43d4c 100644
--- 
a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ForkedBooter.java
+++ 
b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ForkedBooter.java
@@ -34,6 +34,8 @@ import org.apache.maven.surefire.suite.RunResult;
 import org.apache.maven.surefire.testset.TestSetFailedException;
 import org.apache.maven.surefire.util.ReflectionUtils;
 
+import static 
org.apache.maven.surefire.util.internal.StringUtils.encodeStringForForkCommunication;
+
 /**
  * The part of the booter that is unique to a forked vm.
  * <p/>
@@ -109,19 +111,19 @@ public class ForkedBooter
                     new LegacyPojoStackTraceWriter( "test subystem", "no 
method", t.getTargetException() );
                 StringBuilder stringBuilder = new StringBuilder();
                 ForkingRunListener.encode( stringBuilder, stackTraceWriter, 
false );
-                originalOut.println( ( (char) 
ForkingRunListener.BOOTERCODE_ERROR )
-                                     + ",0," + stringBuilder.toString() );
+                encodeAndWriteToOutput( ( (char) 
ForkingRunListener.BOOTERCODE_ERROR )
+                                     + ",0," + stringBuilder.toString() + "\n" 
, originalOut );
             }
             catch ( Throwable t )
             {
                 StackTraceWriter stackTraceWriter = new 
LegacyPojoStackTraceWriter( "test subystem", "no method", t );
                 StringBuilder stringBuilder = new StringBuilder();
                 ForkingRunListener.encode( stringBuilder, stackTraceWriter, 
false );
-                originalOut.println( ( (char) 
ForkingRunListener.BOOTERCODE_ERROR )
-                                     + ",0," + stringBuilder.toString() );
+                encodeAndWriteToOutput( ( (char) 
ForkingRunListener.BOOTERCODE_ERROR )
+                                     + ",0," + stringBuilder.toString() + 
"\n", originalOut );
             }
             // Say bye.
-            originalOut.println( ( (char) ForkingRunListener.BOOTERCODE_BYE ) 
+ ",0,BYE!" );
+            encodeAndWriteToOutput( ( (char) ForkingRunListener.BOOTERCODE_BYE 
) + ",0,BYE!\n", originalOut );
             originalOut.flush();
             // noinspection CallToSystemExit
             exit( 0 );
@@ -136,6 +138,12 @@ public class ForkedBooter
         }
     }
 
+    private static void encodeAndWriteToOutput( String string, PrintStream out 
)
+    {
+        byte[] encodeBytes = encodeStringForForkCommunication( string );
+        out.write( encodeBytes, 0, encodeBytes.length );
+    }
+
     private static final long SYSTEM_EXIT_TIMEOUT = 30 * 1000;
 
     private static void exit( final int returnCode )

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/c26bc4b9/surefire-booter/src/main/java/org/apache/maven/surefire/booter/LazyTestsToRun.java
----------------------------------------------------------------------
diff --git 
a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/LazyTestsToRun.java
 
b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/LazyTestsToRun.java
index 404e5fa..b0d9728 100644
--- 
a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/LazyTestsToRun.java
+++ 
b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/LazyTestsToRun.java
@@ -24,6 +24,7 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.io.PrintStream;
+import java.io.UnsupportedEncodingException;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Iterator;
@@ -32,6 +33,9 @@ import java.util.List;
 import org.apache.maven.surefire.util.ReflectionUtils;
 import org.apache.maven.surefire.util.TestsToRun;
 
+import static 
org.apache.maven.surefire.util.internal.StringUtils.FORK_STREAM_CHARSET_NAME;
+import static 
org.apache.maven.surefire.util.internal.StringUtils.encodeStringForForkCommunication;
+
 /**
  * A variant of TestsToRun that is provided with test class names
  * from an {@link InputStream} (e.g. {@code System.in}). The method
@@ -64,7 +68,14 @@ class LazyTestsToRun
 
         this.originalOutStream = originalOutStream;
 
-        inputReader = new BufferedReader( new InputStreamReader( testSource ) 
);
+        try
+        {
+            inputReader = new BufferedReader( new InputStreamReader( 
testSource, FORK_STREAM_CHARSET_NAME ) );
+        }
+        catch ( UnsupportedEncodingException e )
+        {
+            throw new RuntimeException( "The JVM must support Charset " + 
FORK_STREAM_CHARSET_NAME, e );
+        }
     }
 
     protected void addWorkItem( String className )
@@ -77,7 +88,9 @@ class LazyTestsToRun
 
     protected void requestNextTest()
     {
-        originalOutStream.print( ( (char) 
ForkingRunListener.BOOTERCODE_NEXT_TEST ) + ",0,want more!\n" );
+        byte[] encoded =
+            encodeStringForForkCommunication( ( (char) 
ForkingRunListener.BOOTERCODE_NEXT_TEST ) + ",0,want more!\n" );
+        originalOutStream.write( encoded, 0, encoded.length );
     }
 
     private class BlockingIterator

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/c26bc4b9/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/ConsoleOutputIT.java
----------------------------------------------------------------------
diff --git 
a/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/ConsoleOutputIT.java
 
b/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/ConsoleOutputIT.java
index cfce481..830341d 100644
--- 
a/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/ConsoleOutputIT.java
+++ 
b/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/ConsoleOutputIT.java
@@ -19,6 +19,8 @@ package org.apache.maven.surefire.its;
  * under the License.
  */
 
+import java.nio.charset.Charset;
+
 import org.apache.maven.surefire.its.fixture.OutputValidator;
 import org.apache.maven.surefire.its.fixture.SurefireJUnit4IntegrationTestCase;
 import org.apache.maven.surefire.its.fixture.TestFile;
@@ -33,13 +35,48 @@ public class ConsoleOutputIT
     extends SurefireJUnit4IntegrationTestCase
 {
     @Test
-    public void properNewlines()
+    public void properNewlinesAndEncodingWithDefaultEncodings()
+    {
+        final OutputValidator outputValidator =
+            unpack( "/consoleOutput" ).forkOnce().executeTest();
+
+        validate( outputValidator, true );
+    }
+
+    @Test
+    public void properNewlinesAndEncodingWithDifferentEncoding()
     {
         final OutputValidator outputValidator =
-            unpack( "/consoleOutput" ).redirectToFile( true ).setJUnitVersion( 
"4.7" ).executeTest();
-        TestFile surefireReportsFile = outputValidator.getSurefireReportsFile( 
"consoleOutput.Test1-output.txt" );
-        surefireReportsFile.assertContainsText( "SoutAgain" );
-        surefireReportsFile.assertContainsText( "Printline in shutdown hook" );
+            unpack( "/consoleOutput" ).forkOnce().argLine( 
"-Dfile.encoding=UTF-16" ).executeTest();
+
+        validate( outputValidator, true );
+    }
+
+    @Test
+    public void properNewlinesAndEncodingWithoutFork()
+    {
+        final OutputValidator outputValidator =
+            unpack( "/consoleOutput" ).forkNever().executeTest();
+
+        validate( outputValidator, false );
+    }
+
+    private void validate( final OutputValidator outputValidator, boolean 
includeShutdownHook )
+    {
+        TestFile xmlReportFile = outputValidator.getSurefireReportsXmlFile( 
"TEST-consoleOutput.Test1.xml" );
+        xmlReportFile.assertContainsText( "SoutLine" );
+        xmlReportFile.assertContainsText( "äöüß" );
+        xmlReportFile.assertContainsText( "failing with ü" );
+
+        TestFile outputFile = outputValidator.getSurefireReportsFile( 
"consoleOutput.Test1-output.txt" );
+        outputFile.assertContainsText( "SoutAgain" );
+        outputFile.assertContainsText( "SoutLine" );
+        outputFile.assertContainsText( "äöüß" );
+
+        if ( includeShutdownHook )
+        {
+            outputFile.assertContainsText( "Printline in shutdown hook" );
+        }
     }
 
     @Test

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/c26bc4b9/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/fixture/TestFile.java
----------------------------------------------------------------------
diff --git 
a/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/fixture/TestFile.java
 
b/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/fixture/TestFile.java
index 3c76c62..61736df 100644
--- 
a/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/fixture/TestFile.java
+++ 
b/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/fixture/TestFile.java
@@ -51,7 +51,7 @@ public class TestFile
         this( file, Charset.defaultCharset(), surefireVerifier);
     }
 
-    public TestFile( File file, Charset charset,OutputValidator 
surefireVerifier )
+    public TestFile( File file, Charset charset, OutputValidator 
surefireVerifier )
     {
         this.file = file;
         this.encoding = charset;

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/c26bc4b9/surefire-integration-tests/src/test/resources/consoleOutput/pom.xml
----------------------------------------------------------------------
diff --git 
a/surefire-integration-tests/src/test/resources/consoleOutput/pom.xml 
b/surefire-integration-tests/src/test/resources/consoleOutput/pom.xml
index b19dbd2..2602eef 100644
--- a/surefire-integration-tests/src/test/resources/consoleOutput/pom.xml
+++ b/surefire-integration-tests/src/test/resources/consoleOutput/pom.xml
@@ -20,7 +20,7 @@
       <dependency>
           <groupId>junit</groupId>
           <artifactId>junit</artifactId>
-          <version>${junit.version}</version>
+          <version>4.7</version>
       </dependency>
   </dependencies>
   <build>
@@ -29,23 +29,18 @@
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-surefire-plugin</artifactId>
            <configuration>
+             <redirectTestOutputToFile>true</redirectTestOutputToFile>
+             <testFailureIgnore>true</testFailureIgnore>
              <forkMode>${forkMode}</forkMode>
-             <printSummary>${printSummary}</printSummary>
-             <reportFormat>${reportFormat}</reportFormat>
              <includes>
                 <include>**/Test*.java</include>
              </includes>
            </configuration>
         </plugin>
      </plugins>
-
   </build>
 
-    <properties>
-      <junit.version>4.8.1</junit.version>
-      <forkMode>once</forkMode>
-      <printSummary>true</printSummary>
-      <reportFormat>brief</reportFormat>
-    </properties>
-
+  <properties>
+    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+  </properties>
 </project>

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/c26bc4b9/surefire-integration-tests/src/test/resources/consoleOutput/src/test/java/consoleOutput/Test1.java
----------------------------------------------------------------------
diff --git 
a/surefire-integration-tests/src/test/resources/consoleOutput/src/test/java/consoleOutput/Test1.java
 
b/surefire-integration-tests/src/test/resources/consoleOutput/src/test/java/consoleOutput/Test1.java
index 2682c6e..8994251 100644
--- 
a/surefire-integration-tests/src/test/resources/consoleOutput/src/test/java/consoleOutput/Test1.java
+++ 
b/surefire-integration-tests/src/test/resources/consoleOutput/src/test/java/consoleOutput/Test1.java
@@ -23,6 +23,8 @@ import org.junit.AfterClass;
 import org.junit.BeforeClass;
 import org.junit.Test;
 
+import static org.junit.Assert.fail;
+
 public class Test1
 {
     static
@@ -47,32 +49,33 @@ public class Test1
     public Test1(){
        System.out.println("In constructor");
     }
+
     @Test
-    public void testSystemOut()
+    public void testStdOut()
     {
-
         char c = 'C';
         System.out.print( "Sout" );
         System.out.print( "Again" );
         System.out.print( "\n" );
         System.out.print( c );
         System.out.println( "SoutLine" );
-        System.out.println( "A" );
+        System.out.println( "äöüß" );
         System.out.println( "" );
         System.out.println( "==END==" );
-    }
 
+        fail( "failing with ü" );
+    }
+        
     @Test
-    public void testSystemErr()
+    public void testStdErr()
     {
         char e = 'E';
         System.err.print( "Serr" );
         System.err.print( "\n" );
         System.err.print( e );
         System.err.println( "SerrLine" );
-        System.err.println( "A" );
+        System.err.println( "äöüß" );
         System.err.println( "" );
         System.err.println( "==END==" );
     }
-
 }

Reply via email to