Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SUREFIRE-2058] Corrupted STDOUT by directly writing to native stream in forked JVM 1 with UTF-8 console logging #518

Merged
merged 3 commits into from Apr 26, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -326,11 +326,8 @@ private String readString( @Nonnull final Memento memento, @Nonnegative final in
}
while ( isLastChunk && bytesToDecode > 0 && output.hasRemaining() );

if ( isLastChunk || !output.hasRemaining() )
{
strings.add( ( (Buffer) output ).flip().toString() );
( (Buffer) output ).clear();
}
strings.add( ( (Buffer) output ).flip().toString() );
( (Buffer) output ).clear();
}

memento.getDecoder().reset();
Expand Down
Expand Up @@ -29,7 +29,6 @@
import java.nio.CharBuffer;
import java.nio.channels.ReadableByteChannel;
import java.nio.charset.CharsetDecoder;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
Expand All @@ -51,6 +50,7 @@
import static java.nio.charset.StandardCharsets.ISO_8859_1;
import static java.nio.charset.StandardCharsets.US_ASCII;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.Collections.emptyMap;
import static java.util.Collections.singletonMap;
import static org.apache.maven.surefire.api.booter.Constants.DEFAULT_STREAM_ENCODING;
import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_STDOUT;
Expand Down Expand Up @@ -196,8 +196,7 @@ public void shouldReadInt() throws Exception
{
Channel channel = new Channel( new byte[] {0x01, 0x02, 0x03, 0x04, ':'}, 1 );

Mock thread = new Mock( channel, new MockForkNodeArguments(),
Collections.<Segment, ForkedProcessEventType>emptyMap() );
Mock thread = new Mock( channel, new MockForkNodeArguments(), emptyMap() );

Memento memento = thread.new Memento();

Expand All @@ -210,8 +209,7 @@ public void shouldReadInteger() throws Exception
{
Channel channel = new Channel( new byte[] {(byte) 0xff, 0x01, 0x02, 0x03, 0x04, ':'}, 1 );

Mock thread = new Mock( channel, new MockForkNodeArguments(),
Collections.<Segment, ForkedProcessEventType>emptyMap() );
Mock thread = new Mock( channel, new MockForkNodeArguments(), emptyMap() );

Memento memento = thread.new Memento();
assertThat( thread.readInteger( memento ) )
Expand All @@ -223,8 +221,7 @@ public void shouldReadNullInteger() throws Exception
{
Channel channel = new Channel( new byte[] {(byte) 0x00, ':'}, 1 );

Mock thread = new Mock( channel, new MockForkNodeArguments(),
Collections.<Segment, ForkedProcessEventType>emptyMap() );
Mock thread = new Mock( channel, new MockForkNodeArguments(), emptyMap() );

Memento memento = thread.new Memento();
assertThat( thread.readInteger( memento ) )
Expand All @@ -237,8 +234,7 @@ public void shouldNotReadString() throws Exception
Channel channel = new Channel( PATTERN1.getBytes(), PATTERN1.length() );
channel.read( ByteBuffer.allocate( 100 ) );

Mock thread = new Mock( channel, new MockForkNodeArguments(),
Collections.<Segment, ForkedProcessEventType>emptyMap() );
Mock thread = new Mock( channel, new MockForkNodeArguments(), emptyMap() );

Memento memento = thread.new Memento();
invokeMethod( thread, "readString", memento, 10 );
Expand All @@ -249,15 +245,62 @@ public void shouldReadString() throws Exception
{
Channel channel = new Channel( PATTERN1.getBytes(), PATTERN1.length() );

Mock thread = new Mock( channel, new MockForkNodeArguments(),
Collections.<Segment, ForkedProcessEventType>emptyMap() );
Mock thread = new Mock( channel, new MockForkNodeArguments(), emptyMap() );

Memento memento = thread.new Memento();
String s = invokeMethod( thread, "readString", memento, 10 );
assertThat( s )
.isEqualTo( "0123456789" );
}

@Test
public void shouldReadStringOverflowOnNewLine() throws Exception
{
StringBuilder s = new StringBuilder( 1025 );
for ( int i = 0; i < 10; i++ )
{
s.append( PATTERN1 );
}
s.append( PATTERN1, 0, 23 );
s.append( "\u00FA\n" ); // 2-bytes encoded character + LF

Channel channel = new Channel( s.toString().getBytes( UTF_8 ), s.length() );

Mock thread = new Mock( channel, new MockForkNodeArguments(), emptyMap() );

Memento memento = thread.new Memento();

assertThat( (String) invokeMethod( thread, "readString", memento, 1026 ) )
.isEqualTo( s.toString() );

assertThat ( memento.getByteBuffer().remaining() )
.isEqualTo( 0 );
}

@Test
public void shouldReadStringOverflowOn4BytesEncodedSymbol() throws Exception
{
StringBuilder s = new StringBuilder( 1025 );
for ( int i = 0; i < 10; i++ )
{
s.append( PATTERN1 );
}
s.append( PATTERN1, 0, 23 );
s.append( "\uD83D\uDE35" ); // 4-bytes encoded character

Channel channel = new Channel( s.toString().getBytes( UTF_8 ), s.length() );

Mock thread = new Mock( channel, new MockForkNodeArguments(), emptyMap() );

Memento memento = thread.new Memento();

assertThat( (String) invokeMethod( thread, "readString", memento, 1027 ) )
.isEqualTo( s.toString() );

assertThat ( memento.getByteBuffer().remaining() )
.isEqualTo( 0 );
}

@Test
public void shouldReadStringShiftedBuffer() throws Exception
{
Expand All @@ -269,8 +312,7 @@ public void shouldReadStringShiftedBuffer() throws Exception

Channel channel = new Channel( s.toString().getBytes( UTF_8 ), s.length() );

Mock thread = new Mock( channel, new MockForkNodeArguments(),
Collections.<Segment, ForkedProcessEventType>emptyMap() );
Mock thread = new Mock( channel, new MockForkNodeArguments(), emptyMap() );

Memento memento = thread.new Memento();
// whatever position will be compacted to 0
Expand All @@ -291,8 +333,7 @@ public void shouldReadStringShiftedInput() throws Exception
Channel channel = new Channel( s.toString().getBytes( UTF_8 ), s.length() );
channel.read( ByteBuffer.allocate( 997 ) );

Mock thread = new Mock( channel, new MockForkNodeArguments(),
Collections.<Segment, ForkedProcessEventType>emptyMap() );
Mock thread = new Mock( channel, new MockForkNodeArguments(), emptyMap() );

Memento memento = thread.new Memento();
assertThat( (String) invokeMethod( thread, "readString", memento, PATTERN1.length() ) )
Expand All @@ -312,8 +353,7 @@ public void shouldReadMultipleStringsAndShiftedInput() throws Exception
Channel channel = new Channel( s.toString().getBytes( UTF_8 ), s.length() );
channel.read( ByteBuffer.allocate( 1997 ) );

Mock thread = new Mock( channel, new MockForkNodeArguments(),
Collections.<Segment, ForkedProcessEventType>emptyMap() );
Mock thread = new Mock( channel, new MockForkNodeArguments(), emptyMap() );

Memento memento = thread.new Memento();
// whatever position will be compacted to 0
Expand Down Expand Up @@ -341,8 +381,7 @@ public void shouldDecode3BytesEncodedSymbol() throws Exception
}

Channel channel = new Channel( input, 64 * 1024 );
Mock thread = new Mock( channel, new MockForkNodeArguments(),
Collections.<Segment, ForkedProcessEventType>emptyMap() );
Mock thread = new Mock( channel, new MockForkNodeArguments(), emptyMap() );
Memento memento = thread.new Memento();
String decodedOutput = invokeMethod( thread, "readString", memento, input.length );

Expand Down Expand Up @@ -416,8 +455,7 @@ public void shouldEventTypeReachedMalformedHeader() throws Exception
{
byte[] stream = ":xxxxx-xxxxxxxx-xxxxx:\u000E:xxx".getBytes( UTF_8 );
Channel channel = new Channel( stream, 1 );
Mock thread = new Mock( channel, new MockForkNodeArguments(),
Collections.<Segment, ForkedProcessEventType>emptyMap() );
Mock thread = new Mock( channel, new MockForkNodeArguments(), emptyMap() );

Memento memento = thread.new Memento();
memento.setCharset( UTF_8 );
Expand All @@ -429,8 +467,7 @@ public void shouldReadEmptyString() throws Exception
{
byte[] stream = "\u0000\u0000\u0000\u0000::".getBytes( UTF_8 );
Channel channel = new Channel( stream, 1 );
Mock thread = new Mock( channel, new MockForkNodeArguments(),
Collections.<Segment, ForkedProcessEventType>emptyMap() );
Mock thread = new Mock( channel, new MockForkNodeArguments(), emptyMap() );

Memento memento = thread.new Memento();
memento.setCharset( UTF_8 );
Expand All @@ -444,8 +481,7 @@ public void shouldReadNullString() throws Exception
{
byte[] stream = "\u0000\u0000\u0000\u0001:\u0000:".getBytes( UTF_8 );
Channel channel = new Channel( stream, 1 );
Mock thread = new Mock( channel, new MockForkNodeArguments(),
Collections.<Segment, ForkedProcessEventType>emptyMap() );
Mock thread = new Mock( channel, new MockForkNodeArguments(), emptyMap() );

Memento memento = thread.new Memento();
memento.setCharset( UTF_8 );
Expand All @@ -459,8 +495,7 @@ public void shouldReadSingleCharString() throws Exception
{
byte[] stream = "\u0000\u0000\u0000\u0001:A:".getBytes( UTF_8 );
Channel channel = new Channel( stream, 1 );
Mock thread = new Mock( channel, new MockForkNodeArguments(),
Collections.<Segment, ForkedProcessEventType>emptyMap() );
Mock thread = new Mock( channel, new MockForkNodeArguments(), emptyMap() );

Memento memento = thread.new Memento();
memento.setCharset( UTF_8 );
Expand All @@ -474,8 +509,7 @@ public void shouldReadThreeCharactersString() throws Exception
{
byte[] stream = "\u0000\u0000\u0000\u0003:ABC:".getBytes( UTF_8 );
Channel channel = new Channel( stream, 1 );
Mock thread = new Mock( channel, new MockForkNodeArguments(),
Collections.<Segment, ForkedProcessEventType>emptyMap() );
Mock thread = new Mock( channel, new MockForkNodeArguments(), emptyMap() );

Memento memento = thread.new Memento();
memento.setCharset( UTF_8 );
Expand All @@ -489,8 +523,7 @@ public void shouldReadDefaultCharset() throws Exception
{
byte[] stream = "\u0005:UTF-8:".getBytes( US_ASCII );
Channel channel = new Channel( stream, 1 );
Mock thread = new Mock( channel, new MockForkNodeArguments(),
Collections.<Segment, ForkedProcessEventType>emptyMap() );
Mock thread = new Mock( channel, new MockForkNodeArguments(), emptyMap() );

Memento memento = thread.new Memento();
memento.setCharset( UTF_8 );
Expand All @@ -505,8 +538,7 @@ public void shouldReadNonDefaultCharset() throws Exception
{
byte[] stream = ( (char) 10 + ":ISO_8859_1:" ).getBytes( US_ASCII );
Channel channel = new Channel( stream, 1 );
Mock thread = new Mock( channel, new MockForkNodeArguments(),
Collections.<Segment, ForkedProcessEventType>emptyMap() );
Mock thread = new Mock( channel, new MockForkNodeArguments(), emptyMap() );

Memento memento = thread.new Memento();
memento.setCharset( UTF_8 );
Expand All @@ -521,10 +553,9 @@ public void shouldSetNonDefaultCharset()
{
byte[] stream = {};
Channel channel = new Channel( stream, 1 );
Mock thread = new Mock( channel, new MockForkNodeArguments(),
Collections.<Segment, ForkedProcessEventType>emptyMap() );
Memento memento = thread.new Memento();
Mock thread = new Mock( channel, new MockForkNodeArguments(), emptyMap() );

Memento memento = thread.new Memento();
memento.setCharset( ISO_8859_1 );
assertThat( memento.getDecoder().charset() ).isEqualTo( ISO_8859_1 );

Expand All @@ -541,8 +572,7 @@ public void malformedCharset() throws Exception
{
byte[] stream = ( (char) 8 + ":ISO_8859:" ).getBytes( US_ASCII );
Channel channel = new Channel( stream, 1 );
Mock thread = new Mock( channel, new MockForkNodeArguments(),
Collections.<Segment, ForkedProcessEventType>emptyMap() );
Mock thread = new Mock( channel, new MockForkNodeArguments(), emptyMap() );

Memento memento = thread.new Memento();
memento.setCharset( UTF_8 );
Expand Down