Skip to content

Commit

Permalink
[SUREFIRE-1137] Ensure that all communication with the forked process…
Browse files Browse the repository at this point in the history
… 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
  • Loading branch information
agudian committed Feb 5, 2015
1 parent 2b4629c commit c26bc4b
Show file tree
Hide file tree
Showing 16 changed files with 220 additions and 100 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
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;
Expand Down Expand Up @@ -68,6 +69,7 @@
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;

Expand Down Expand Up @@ -468,7 +470,8 @@ private RunResult fork( Object testSet, KeyValueSource providerProperties, ForkC
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" );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
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/>
Expand Down Expand Up @@ -90,7 +92,7 @@ public synchronized int read()
String currentElement = testItemQueue.poll();
if ( currentElement != null )
{
currentBuffer = currentElement.getBytes();
currentBuffer = encodeStringForForkCommunication( currentElement );
currentPos = 0;
}
else
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
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;
Expand Down Expand Up @@ -140,14 +141,10 @@ public void consumeLine( String s )
}
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 ) );
Expand Down Expand Up @@ -184,6 +181,30 @@ public void consumeLine( String s )
}
}

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
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,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;

/**
Expand Down Expand Up @@ -47,7 +52,16 @@ public DirectConsoleOutput( PrintStream sout, PrintStream serr )
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 )
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,11 @@
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;
Expand Down Expand Up @@ -72,14 +74,14 @@ public void testHeaderCreation()
{
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()
Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
*/

import java.io.PrintStream;
import java.nio.charset.Charset;
import java.util.Enumeration;
import java.util.Properties;

Expand All @@ -31,6 +32,8 @@
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/>
Expand Down Expand Up @@ -81,7 +84,6 @@ public class ForkingRunListener

public static final byte BOOTERCODE_BYE = (byte) 'Z';


private final PrintStream target;

private final int testSetChannelId;
Expand All @@ -104,42 +106,42 @@ public ForkingRunListener( PrintStream target, int testSetChannelId, boolean tri

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()
Expand All @@ -148,7 +150,7 @@ void sendProps()

if ( systemProperties != null )
{
Enumeration propertyKeys = systemProperties.propertyNames();
Enumeration<?> propertyKeys = systemProperties.propertyNames();

while ( propertyKeys.hasMoreElements() )
{
Expand All @@ -160,7 +162,7 @@ void sendProps()
{
value = "null";
}
target.print( toPropertyString( key, value ) );
encodeAndWriteToTarget( toPropertyString( key, value ) );
}
}
}
Expand All @@ -182,34 +184,14 @@ public void writeTestOutput( byte[] buf, int off, int len, boolean stdout )

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 )
Expand All @@ -220,10 +202,16 @@ public void info( String message )
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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
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.
Expand Down Expand Up @@ -78,17 +77,14 @@ public void write( int b )
}
}

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()
Expand Down
Loading

0 comments on commit c26bc4b

Please sign in to comment.