Updated Branches: refs/heads/master b9c5c8614 -> 5b6e64552
o Simpler encoding of sysout/syserr byte array from fork to fork client Project: http://git-wip-us.apache.org/repos/asf/maven-surefire/repo Commit: http://git-wip-us.apache.org/repos/asf/maven-surefire/commit/5b6e6455 Tree: http://git-wip-us.apache.org/repos/asf/maven-surefire/tree/5b6e6455 Diff: http://git-wip-us.apache.org/repos/asf/maven-surefire/diff/5b6e6455 Branch: refs/heads/master Commit: 5b6e64552ccb750c561476358bdd384965f93db0 Parents: b9c5c86 Author: Andreas Gudian <agud...@apache.org> Authored: Sun Jul 28 22:05:17 2013 +0200 Committer: Andreas Gudian <agud...@apache.org> Committed: Sun Jul 28 22:05:17 2013 +0200 ---------------------------------------------------------------------- .../booterclient/output/ForkClient.java | 12 +- .../surefire/booter/ForkingRunListener.java | 6 +- .../surefire/util/internal/StringUtils.java | 540 ++++--------------- .../surefire/util/internal/StringUtilsTest.java | 42 +- 4 files changed, 136 insertions(+), 464 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/5b6e6455/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 fa2f41d..3ed4110 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 @@ -44,7 +44,7 @@ import org.apache.maven.surefire.util.internal.StringUtils; /** * Knows how to reconstruct *all* the state transmitted over stdout by the forked process. - * + * * @author Kristian Rosenvold */ public class ForkClient @@ -135,13 +135,13 @@ public class ForkClient } break; case ForkingRunListener.BOOTERCODE_STDOUT: - byte[] bytes = new byte[remaining.length() * 2]; - int len = StringUtils.unescapeJava( bytes, remaining ); + byte[] bytes = new byte[remaining.length()]; + int len = StringUtils.unescapeBytes( bytes, remaining ); getOrCreateConsoleOutputReceiver( channelNumber ).writeTestOutput( bytes, 0, len, true ); break; case ForkingRunListener.BOOTERCODE_STDERR: - bytes = new byte[remaining.length() * 2]; - len = StringUtils.unescapeJava( bytes, remaining ); + bytes = new byte[remaining.length()]; + len = StringUtils.unescapeBytes( bytes, remaining ); getOrCreateConsoleOutputReceiver( channelNumber ).writeTestOutput( bytes, 0, len, false ); break; case ForkingRunListener.BOOTERCODE_CONSOLE: @@ -242,7 +242,7 @@ public class ForkClient /** * Used when getting reporters on the plugin side of a fork. - * + * * @param channelNumber The logical channel number * @return A mock provider reporter */ http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/5b6e6455/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 9a8beec..e515fd8 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 @@ -169,8 +169,8 @@ public class ForkingRunListener { byte[] header = stdout ? stdOutHeader : stdErrHeader; byte[] content = - new byte[buf.length * 6 + 1]; // Unicode escapes can be up to 6 times length of regular char. Yuck. - int i = StringUtils.escapeJavaStyleString( content, 0, buf, off, len ); + new byte[buf.length * 3 + 1]; // Hex-escaping can be up to 3 times length of a regular byte. + int i = StringUtils.escapeBytesToPrintable( content, 0, buf, off, len ); content[i++] = (byte) '\n'; synchronized ( target ) // See notes about synhronization/thread safety in class javadoc @@ -219,7 +219,7 @@ public class ForkingRunListener byteBuffer.append( testSetChannelId ); byteBuffer.comma(); final int i = - StringUtils.escapeJavaStyleString( byteBuffer.getData(), byteBuffer.getlength(), buf, 0, buf.length ); + StringUtils.escapeBytesToPrintable( byteBuffer.getData(), byteBuffer.getlength(), buf, 0, buf.length ); byteBuffer.advance( i ); byteBuffer.append( '\n' ); synchronized ( target ) http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/5b6e6455/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 4591f7a..99d087d 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 @@ -20,18 +20,20 @@ package org.apache.maven.surefire.util.internal; */ import java.io.IOException; -import java.io.PrintStream; import java.io.StringWriter; import java.io.Writer; import java.util.StringTokenizer; + import org.apache.maven.surefire.util.NestedRuntimeException; /** - * <p>Common <code>String</code> manipulation routines.</p> + * <p> + * Common <code>String</code> manipulation routines. + * </p> * <p/> - * <p>Originally from - * <a href="http://jakarta.apache.org/turbine/">Turbine</a> and the - * GenerationJavaCore library.</p> + * <p> + * Originally from <a href="http://jakarta.apache.org/turbine/">Turbine</a> and the GenerationJavaCore library. + * </p> * * @author <a href="mailto:j...@latchkey.com">Jon S. Stevens</a> * @author <a href="mailto:d...@finemaltcoding.com">Daniel Rall</a> @@ -46,18 +48,19 @@ import org.apache.maven.surefire.util.NestedRuntimeException; * @author <a href="mailto:vincent.sive...@gmail.com">Vincent Siveton</a> * @version $Id: StringUtils.java 8001 2009-01-03 13:17:09Z vsiveton $ * @noinspection JavaDoc - * <p/> - * A quick borrow from plexus-utils by Kristian Rosenvold, to restore jdk1.3 compat - * Threw away all the unused stuff. - * <p/> - * NOTE: This class is not part of any api and is public purely for technical reasons ! + * <p/> + * A quick borrow from plexus-utils by Kristian Rosenvold, to restore jdk1.3 compat Threw away all the + * unused stuff. + * <p/> + * NOTE: This class is not part of any api and is public purely for technical reasons ! * @since 1.0 */ public class StringUtils { + private static final byte[] HEX_CHARS = new byte[] { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + 'A', 'B', 'C', 'D', 'E', 'F' }; - // Splitting - //-------------------------------------------------------------------------- public static String[] split( String text, String separator ) { @@ -107,20 +110,19 @@ public class StringUtils return list; } - /** - * <p>Checks if a (trimmed) String is <code>null</code> or blank.</p> + * <p> + * Checks if a (trimmed) String is <code>null</code> or blank. + * </p> * * @param str the String to check - * @return <code>true</code> if the String is <code>null</code>, or - * length zero once trimmed + * @return <code>true</code> if the String is <code>null</code>, or length zero once trimmed */ public static boolean isBlank( String str ) { return ( ( str == null ) || ( str.trim().length() == 0 ) ); } - // Ripped from commons-lang StringEscapeUtils. Maybe Use dependency instead public static void unescapeJava( StringWriter out, String str ) { @@ -220,113 +222,39 @@ public class StringUtils } } - // Ripped from commons-lang StringEscapeUtils. Maybe Use dependency instead - public static int unescapeJava( byte[] out, String str ) + + + // Ripped from commons-lang StringEscapeUtils. With a minor modification, we unicode-quote commas + // to avoid csv decoding problems ;) + + /** + * Courtesy of commons-lang StringEscapeUtils, slightly modified, see below + * + * @param str String to escape values in, may be null + * @return the escaped string + */ + public static void escapeJavaStyleString( StringBuffer target, String str ) { - int outPos = 0; - if ( out == null ) - { - throw new IllegalArgumentException( "The Writer must not be null" ); - } if ( str == null ) { - return 0; + return; } - int sz = str.length(); - StringBuffer unicode = new StringBuffer( 4 ); - boolean hadSlash = false; - boolean inUnicode = false; - for ( int i = 0; i < sz; i++ ) + try { - char ch = str.charAt( i ); - if ( inUnicode ) - { - // if in unicode, then we're reading unicode - // values in somehow - unicode.append( ch ); - if ( unicode.length() == 4 ) - { - // unicode now contains the four hex digits - // which represents our unicode character - try - { - int value = Integer.parseInt( unicode.toString(), 16 ); - out[outPos++] = (byte) value; - unicode.setLength( 0 ); - inUnicode = false; - hadSlash = false; - } - catch ( NumberFormatException nfe ) - { - throw new NestedRuntimeException( "Unable to parse unicode value: " + unicode, nfe ); - } - } - continue; - } - if ( hadSlash ) - { - // handle an escaped value - hadSlash = false; - switch ( ch ) - { - case '\\': - out[outPos++] = '\\'; - break; - case '\'': - out[outPos++] = '\''; - break; - case '\"': - out[outPos++] = '"'; - break; - case 'r': - out[outPos++] = '\r'; - break; - case 'f': - out[outPos++] = '\f'; - break; - case 't': - out[outPos++] = '\t'; - break; - case 'n': - out[outPos++] = '\n'; - break; - case 'b': - out[outPos++] = '\b'; - break; - case 'u': - { - // uh-oh, we're in unicode country.... - inUnicode = true; - break; - } - default: - out[outPos++] = (byte) ch; - break; - } - continue; - } - else if ( ch == '\\' ) - { - hadSlash = true; - continue; - } - out[outPos++] = (byte) ch; + StringWriter writer = new StringWriter( str.length() * 2 ); + escapeJavaStyleString( writer, str, true ); + target.append( writer.toString() ); // todo: be bit smarter } - if ( hadSlash ) + catch ( IOException ioe ) { - // then we're in the weird case of a \ at the end of the - // string, let's output it anyway. - out[outPos++] = '\\'; + // this should never ever happen while writing to a StringWriter + ioe.printStackTrace(); } - return outPos; } - // Ripped from commons-lang StringEscapeUtils. With a minor modification, we unicode-quote commas - // to avoid csv decoding problems ;) - /** - * @param out write to receieve the escaped string - * @param str String to escape values in, may be null + * @param out write to receieve the escaped string + * @param str String to escape values in, may be null * @param escapeSingleQuote escapes single quotes if <code>true</code> * @throws java.io.IOException if an IOException occurs */ @@ -357,7 +285,7 @@ public class StringUtils out.write( "\\u0" + hex( ch ) ); } else if ( ch > 0x7f || ch == ',' ) - { // Kr - this line modified from commons + { // Kr - this line modified from commons out.write( "\\u00" + hex( ch ) ); } else if ( ch < 32 ) @@ -427,202 +355,36 @@ public class StringUtils } } - public static void escapeJavaStyleString( ByteBuffer out, byte[] str, int off, int len ) - { - if ( out == null ) - { - throw new IllegalArgumentException( "The Writer must not be null" ); - } - final int inputLength = str.length; - if ( str == null || inputLength == 0 ) - { - return; - } - int outputPos = 0; - int end = off + len; - for ( int i = off; i < end; i++ ) - { - char ch = (char) str[i]; - // handle unicode - if ( ch > 0xfff ) - { - outputPos = writeOut( out, outputPos, "\\u" + hex( ch ) ); - } - else if ( ch > 0xff ) - { - outputPos = writeOut( out, outputPos, "\\u0" + hex( ch ) ); - } - else if ( ch > 0x7f || ch == ',' ) - { // Kr - this line modified from commons - outputPos = writeOut( out, outputPos, "\\u00" + hex( ch ) ); - } - else if ( ch < 32 ) - { - switch ( ch ) - { - case '\b': - out.append( '\\' ); - out.append( 'b' ); - break; - case '\n': - out.append( '\\' ); - out.append( 'n' ); - break; - case '\t': - out.append( '\\' ); - out.append( 't' ); - break; - case '\f': - out.append( '\\' ); - out.append( 'f' ); - break; - case '\r': - out.append( '\\' ); - out.append( 'r' ); - break; - default: - if ( ch > 0xf ) - { - outputPos = writeOut( out, outputPos, "\\u00" + hex( ch ) ); - } - else - { - outputPos = writeOut( out, outputPos, "\\u000" + hex( ch ) ); - } - break; - } - } - else - { - switch ( ch ) - { - case '\'': - out.append( '\\' ); - out.append( '\'' ); - break; - case '"': - out.append( '\\' ); - out.append( '"' ); - break; - case '\\': - out.append( '\\' ); - out.append( '\\' ); - break; - case '/': - out.append( '\\' ); - out.append( '/' ); - break; - default: - out.append( ch ); - break; - } - } - } - } - - public static void escapeJavaStyleString( PrintStream out, byte[] str, int off, int len ) + public static String hex( char ch ) { - if ( out == null ) - { - throw new IllegalArgumentException( "The Writer must not be null" ); - } - final int inputLength = str.length; - if ( str == null || inputLength == 0 ) - { - return; - } - int outputPos = 0; - int end = off + len; - for ( int i = off; i < end; i++ ) - { - char ch = (char) str[i]; - - // handle unicode - if ( ch > 0xfff ) - { - outputPos = writeOut( out, outputPos, "\\u" + hex( ch ) ); - } - else if ( ch > 0xff ) - { - outputPos = writeOut( out, outputPos, "\\u0" + hex( ch ) ); - } - else if ( ch > 0x7f || ch == ',' ) - { // Kr - this line modified from commons - outputPos = writeOut( out, outputPos, "\\u00" + hex( ch ) ); - } - else if ( ch < 32 ) - { - switch ( ch ) - { - case '\b': - out.append( '\\' ); - out.append( 'b' ); - break; - case '\n': - out.append( '\\' ); - out.append( 'n' ); - break; - case '\t': - out.append( '\\' ); - out.append( 't' ); - break; - case '\f': - out.append( '\\' ); - out.append( 'f' ); - break; - case '\r': - out.append( '\\' ); - out.append( 'r' ); - break; - default: - if ( ch > 0xf ) - { - outputPos = writeOut( out, outputPos, "\\u00" + hex( ch ) ); - } - else - { - outputPos = writeOut( out, outputPos, "\\u000" + hex( ch ) ); - } - break; - } - } - else - { - switch ( ch ) - { - case '\'': - out.append( '\\' ); - out.append( '\'' ); - break; - case '"': - out.append( '\\' ); - out.append( '"' ); - break; - case '\\': - out.append( '\\' ); - out.append( '\\' ); - break; - case '/': - out.append( '\\' ); - out.append( '/' ); - break; - default: - out.append( ch ); - break; - } - } - } + return Integer.toHexString( ch ).toUpperCase(); } - public static int escapeJavaStyleString( byte[] out, int outoff, byte[] str, int off, int len ) + + /** + * Escapes the bytes in the array {@code str} to contain only 'printable' bytes. + * <p> + * Escaping is done by encoding the non-nicely printable bytes to {@code '\' + upperCaseHexBytes(byte)}. + * <p> + * A save length of {@code out} is {@code len * 3 + outoff}. + * <p> + * The reverse-method is {@link #unescapeBytes(byte[], String)}. + * + * @param out output buffer + * @param outoff offset in the output buffer + * @param input input buffer + * @param off offset in the input buffer + * @param len number of bytes to copy from the input buffer + * @return number of bytes written to {@code out} + */ + public static int escapeBytesToPrintable( byte[] out, int outoff, byte[] input, int off, int len ) { if ( out == null ) { - throw new IllegalArgumentException( "The Writer must not be null" ); + throw new IllegalArgumentException( "The output array must not be null" ); } - final int inputLength = str.length; - if ( str == null || inputLength == 0 ) + if ( input == null || input.length == 0 ) { return 0; } @@ -630,165 +392,69 @@ public class StringUtils int end = off + len; for ( int i = off; i < end; i++ ) { - char ch = (char) str[i]; + byte b = input[i]; - // handle unicode - if ( ch > 0xfff ) + // handle non-nicely printable bytes + if ( b < 32 || b > 126 || b == '\\' ) { - outputPos = writeOut( out, outputPos, "\\u" + hex( ch ) ); - } - else if ( ch > 0xff ) - { - outputPos = writeOut( out, outputPos, "\\u0" + hex( ch ) ); - } - else if ( ch > 0x7f || ch == ',' ) - { // Kr - this line modified from commons - outputPos = writeOut( out, outputPos, "\\u00" + hex( ch ) ); - } - else if ( ch < 32 ) - { - switch ( ch ) - { - case '\b': - out[outputPos++] = '\\'; - out[outputPos++] = 'b'; - break; - case '\n': - out[outputPos++] = '\\'; - out[outputPos++] = 'n'; - break; - case '\t': - out[outputPos++] = '\\'; - out[outputPos++] = 't'; - break; - case '\f': - out[outputPos++] = '\\'; - out[outputPos++] = 'f'; - break; - case '\r': - out[outputPos++] = '\\'; - out[outputPos++] = 'r'; - break; - default: - if ( ch > 0xf ) - { - outputPos = writeOut( out, outputPos, "\\u00" + hex( ch ) ); - } - else - { - outputPos = writeOut( out, outputPos, "\\u000" + hex( ch ) ); - } - break; - } + int upper = ( 0xF0 & b ) >> 4; + int lower = ( 0x0F & b ); + out[outputPos++] = '\\'; + out[outputPos++] = HEX_CHARS[upper]; + out[outputPos++] = HEX_CHARS[lower]; } else { - switch ( ch ) - { - case '\'': - out[outputPos++] = '\\'; - out[outputPos++] = '\''; - break; - case '"': - out[outputPos++] = '\\'; - out[outputPos++] = '"'; - break; - case '\\': - out[outputPos++] = '\\'; - out[outputPos++] = '\\'; - break; - case '/': - out[outputPos++] = '\\'; - out[outputPos++] = '/'; - break; - default: - out[outputPos++] = (byte) ch; - break; - } + out[outputPos++] = b; } } - return outputPos - outoff; - } - - private static int writeOut( ByteBuffer out, int outputPos, final String msg ) - { - byte[] bytes = msg.getBytes(); - for ( int cnt = 0; cnt < bytes.length; cnt++ ) - { - out.append( bytes[cnt] ); - } - return outputPos; - } - private static int writeOut( PrintStream out, int outputPos, final String msg ) - { - byte[] bytes = msg.getBytes(); - for ( int cnt = 0; cnt < bytes.length; cnt++ ) - { - out.write( bytes[cnt] ); - } - return outputPos; - } - - - private static int writeOut( byte[] out, int outputPos, final String msg ) - { - byte[] bytes = msg.getBytes(); - for ( int cnt = 0; cnt < bytes.length; cnt++ ) - { - out[outputPos++] = bytes[cnt]; - } - return outputPos; + return outputPos - outoff; } - public static String hex( char ch ) - { - return Integer.toHexString( ch ).toUpperCase(); - } - /** - * Courtesy of commons-lang StringEscapeUtils, slightly modified, see below + * Reverses the effect of {@link #escapeBytesToPrintable(byte[], int, byte[], int, int)}. + * <p> + * A save length of {@code out} is {@code str.length()} * - * @param str String to escape values in, may be null - * @return the escaped string + * @param out the target byte array + * @param str the input String + * @return the number of bytes written to {@code out} */ - public static void escapeJavaStyleString( StringBuffer target, String str ) + public static int unescapeBytes( byte[] out, String str ) { - if ( str == null ) + int outPos = 0; + if ( out == null ) { - return; + throw new IllegalArgumentException( "The output array must not be null" ); } - try + if ( str == null ) { - StringWriter writer = new StringWriter( str.length() * 2 ); - escapeJavaStyleString( writer, str, true ); - target.append( writer.toString() ); // todo: be bit smarter + return 0; } - catch ( IOException ioe ) + for ( int i = 0; i < str.length(); i++ ) { - // this should never ever happen while writing to a StringWriter - ioe.printStackTrace(); + char ch = str.charAt( i ); + + if (ch == '\\') { + int upper = fromHex( str.charAt( ++i )); + int lower = fromHex( str.charAt( ++i )); + out[outPos++] = (byte) (upper << 4 | lower); + } + else { + out[outPos++] = (byte) ch; + } } + return outPos; } - public static void escapeJavaStyleString( PrintStream target, String str ) + private static int fromHex( char c ) { - if ( str == null ) - { - return; - } - try - { - StringWriter writer = new StringWriter( str.length() * 2 ); - escapeJavaStyleString( writer, str, true ); - target.append( writer.toString() ); // todo: be bit smarter - } - catch ( IOException ioe ) - { - // this should never ever happen while writing to a StringWriter - ioe.printStackTrace(); + if ( c <= '9' ) { + return c - '0'; + } else{ + return (c - 'A') + 10; } } } - http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/5b6e6455/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 de3bf02..61012af 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.util.Arrays; + import junit.framework.TestCase; /** @@ -30,26 +32,30 @@ public class StringUtilsTest public void testUnescape() { - byte[] buffer = new byte[80]; - final int abc = StringUtils.unescapeJava( buffer, "ABC" ); - assertEquals( 3, abc ); - } + byte[] input = new byte[256]; - public void testUnescapeWithEscape() - { - byte[] buffer = new byte[80]; - final int abc = StringUtils.unescapeJava( buffer, "AB\tC" ); - assertEquals( 4, abc ); - } + for ( int i = 0; i <= 0xFF; i++ ) + { + byte b = (byte) ( 0xFF & i ); + input[i] = b; + } - public void testEscape() - { - ByteBuffer buffer = new ByteBuffer( 80 ); - StringUtils.escapeJavaStyleString( buffer, "AB\tC".getBytes(), 0, 4 ); - assertEquals( 5, buffer.getlength() ); - String temp = buffer.toString(); - assertEquals( "AB\\tC", temp ); + byte[] escaped = new byte[input.length * 3]; - } + int escapedBytes = StringUtils.escapeBytesToPrintable( escaped, 0, input, 0, input.length ); + String escapedString = new String( escaped, 0, escapedBytes ); + System.out.println( escapedString ); + + assertEquals( escapedBytes, escapedString.length() ); + + byte[] unescaped = new byte[input.length]; + int unescapeBytes = StringUtils.unescapeBytes( unescaped, escapedString ); + + assertEquals( input.length, unescapeBytes ); + + for (int i = 0; i < input.length; i++) { + assertEquals("At position " + i, input[i], unescaped[i]); + } + } }