Coverage Report - org.apache.maven.plugin.surefire.booterclient.output.ForkClient
 
Classes in this File Line Coverage Branch Coverage Complexity
ForkClient
77%
84/108
36%
24/65
3
 
 1  
 package org.apache.maven.plugin.surefire.booterclient.output;
 2  
 
 3  
 /*
 4  
  * Licensed to the Apache Software Foundation (ASF) under one
 5  
  * or more contributor license agreements.  See the NOTICE file
 6  
  * distributed with this work for additional information
 7  
  * regarding copyright ownership.  The ASF licenses this file
 8  
  * to you under the Apache License, Version 2.0 (the
 9  
  * "License"); you may not use this file except in compliance
 10  
  * with the License.  You may obtain a copy of the License at
 11  
  *
 12  
  *     http://www.apache.org/licenses/LICENSE-2.0
 13  
  *
 14  
  * Unless required by applicable law or agreed to in writing,
 15  
  * software distributed under the License is distributed on an
 16  
  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 17  
  * KIND, either express or implied.  See the License for the
 18  
  * specific language governing permissions and limitations
 19  
  * under the License.
 20  
  */
 21  
 
 22  
 import java.io.BufferedReader;
 23  
 import java.io.IOException;
 24  
 import java.io.StringReader;
 25  
 import java.util.Collections;
 26  
 import java.util.HashMap;
 27  
 import java.util.Map;
 28  
 import java.util.Properties;
 29  
 import java.util.StringTokenizer;
 30  
 
 31  
 import org.apache.maven.plugin.surefire.booterclient.lazytestprovider.TestProvidingInputStream;
 32  
 import org.apache.maven.plugin.surefire.report.DefaultReporterFactory;
 33  
 import org.apache.maven.shared.utils.cli.StreamConsumer;
 34  
 import org.apache.maven.surefire.booter.ForkingRunListener;
 35  
 import org.apache.maven.surefire.report.CategorizedReportEntry;
 36  
 import org.apache.maven.surefire.report.ConsoleLogger;
 37  
 import org.apache.maven.surefire.report.ConsoleOutputReceiver;
 38  
 import org.apache.maven.surefire.report.ReportEntry;
 39  
 import org.apache.maven.surefire.report.ReporterException;
 40  
 import org.apache.maven.surefire.report.RunListener;
 41  
 import org.apache.maven.surefire.report.StackTraceWriter;
 42  
 import org.apache.maven.surefire.util.NestedRuntimeException;
 43  
 import org.apache.maven.surefire.util.internal.StringUtils;
 44  
 
 45  
 /**
 46  
  * Knows how to reconstruct *all* the state transmitted over stdout by the forked process.
 47  
  *
 48  
  * @author Kristian Rosenvold
 49  
  */
 50  
 public class ForkClient
 51  
     implements StreamConsumer
 52  
 {
 53  
 
 54  
     private final DefaultReporterFactory defaultReporterFactory;
 55  
 
 56  
     private final TestProvidingInputStream testProvidingInputStream;
 57  
 
 58  15
     private final Map<Integer, RunListener> testSetReporters =
 59  
         Collections.synchronizedMap( new HashMap<Integer, RunListener>() );
 60  
 
 61  
     private final Properties testVmSystemProperties;
 62  
 
 63  15
     private volatile boolean saidGoodBye = false;
 64  
 
 65  15
     private volatile StackTraceWriter errorInFork = null;
 66  
 
 67  
     public ForkClient( DefaultReporterFactory defaultReporterFactory, Properties testVmSystemProperties )
 68  
     {
 69  15
         this( defaultReporterFactory, testVmSystemProperties, null );
 70  15
     }
 71  
 
 72  
     public ForkClient( DefaultReporterFactory defaultReporterFactory, Properties testVmSystemProperties,
 73  
                        TestProvidingInputStream testProvidingInputStream )
 74  15
     {
 75  15
         this.defaultReporterFactory = defaultReporterFactory;
 76  15
         this.testVmSystemProperties = testVmSystemProperties;
 77  15
         this.testProvidingInputStream = testProvidingInputStream;
 78  15
     }
 79  
 
 80  
     public void consumeLine( String s )
 81  
     {
 82  
         try
 83  
         {
 84  994
             if ( s.length() == 0 )
 85  
             {
 86  0
                 return;
 87  
             }
 88  994
             final byte operationId = (byte) s.charAt( 0 );
 89  994
             int commma = s.indexOf( ",", 3 );
 90  994
             if ( commma < 0 )
 91  
             {
 92  0
                 System.out.println( s );
 93  0
                 return;
 94  
             }
 95  994
             final Integer channelNumber = Integer.parseInt( s.substring( 2, commma ), 16 );
 96  994
             int rest = s.indexOf( ",", commma );
 97  994
             final String remaining = s.substring( rest + 1 );
 98  
 
 99  994
             switch ( operationId )
 100  
             {
 101  
                 case ForkingRunListener.BOOTERCODE_TESTSET_STARTING:
 102  2
                     getOrCreateReporter( channelNumber ).testSetStarting( createReportEntry( remaining ) );
 103  2
                     break;
 104  
                 case ForkingRunListener.BOOTERCODE_TESTSET_COMPLETED:
 105  2
                     getOrCreateReporter( channelNumber ).testSetCompleted( createReportEntry( remaining ) );
 106  2
                     break;
 107  
                 case ForkingRunListener.BOOTERCODE_TEST_STARTING:
 108  3
                     getOrCreateReporter( channelNumber ).testStarting( createReportEntry( remaining ) );
 109  3
                     break;
 110  
                 case ForkingRunListener.BOOTERCODE_TEST_SUCCEEDED:
 111  2
                     getOrCreateReporter( channelNumber ).testSucceeded( createReportEntry( remaining ) );
 112  2
                     break;
 113  
                 case ForkingRunListener.BOOTERCODE_TEST_FAILED:
 114  3
                     getOrCreateReporter( channelNumber ).testFailed( createReportEntry( remaining ) );
 115  3
                     break;
 116  
                 case ForkingRunListener.BOOTERCODE_TEST_SKIPPED:
 117  2
                     getOrCreateReporter( channelNumber ).testSkipped( createReportEntry( remaining ) );
 118  2
                     break;
 119  
                 case ForkingRunListener.BOOTERCODE_TEST_ERROR:
 120  1
                     getOrCreateReporter( channelNumber ).testError( createReportEntry( remaining ) );
 121  1
                     break;
 122  
                 case ForkingRunListener.BOOTERCODE_TEST_ASSUMPTIONFAILURE:
 123  1
                     getOrCreateReporter( channelNumber ).testAssumptionFailure( createReportEntry( remaining ) );
 124  1
                     break;
 125  
                 case ForkingRunListener.BOOTERCODE_SYSPROPS:
 126  976
                     int keyEnd = remaining.indexOf( "," );
 127  976
                     StringBuilder key = new StringBuilder();
 128  976
                     StringBuilder value = new StringBuilder();
 129  976
                     StringUtils.unescapeString( key, remaining.substring( 0, keyEnd ) );
 130  976
                     StringUtils.unescapeString( value, remaining.substring( keyEnd + 1 ) );
 131  
 
 132  976
                     synchronized ( testVmSystemProperties )
 133  
                     {
 134  976
                         testVmSystemProperties.put( key.toString(), value.toString() );
 135  976
                     }
 136  976
                     break;
 137  
                 case ForkingRunListener.BOOTERCODE_STDOUT:
 138  1
                     byte[] bytes = new byte[remaining.length()];
 139  1
                     int len = StringUtils.unescapeBytes( bytes, remaining );
 140  1
                     getOrCreateConsoleOutputReceiver( channelNumber ).writeTestOutput( bytes, 0, len, true );
 141  1
                     break;
 142  
                 case ForkingRunListener.BOOTERCODE_STDERR:
 143  0
                     bytes = new byte[remaining.length()];
 144  0
                     len = StringUtils.unescapeBytes( bytes, remaining );
 145  0
                     getOrCreateConsoleOutputReceiver( channelNumber ).writeTestOutput( bytes, 0, len, false );
 146  0
                     break;
 147  
                 case ForkingRunListener.BOOTERCODE_CONSOLE:
 148  1
                     getOrCreateConsoleLogger( channelNumber ).info( createConsoleMessage( remaining ) );
 149  1
                     break;
 150  
                 case ForkingRunListener.BOOTERCODE_NEXT_TEST:
 151  0
                     if ( null != testProvidingInputStream )
 152  
                     {
 153  0
                         testProvidingInputStream.provideNewTest();
 154  
                     }
 155  
                     break;
 156  
                 case ForkingRunListener.BOOTERCODE_ERROR:
 157  0
                     errorInFork = deserializeStackStraceWriter( new StringTokenizer( remaining, "," ) );
 158  0
                     break;
 159  
                 case ForkingRunListener.BOOTERCODE_BYE:
 160  0
                     saidGoodBye = true;
 161  0
                     break;
 162  
                 default:
 163  0
                     System.out.println( s );
 164  
             }
 165  
         }
 166  0
         catch ( NumberFormatException e )
 167  
         {
 168  0
             System.out.println( s );
 169  
         }
 170  0
         catch ( ReporterException e )
 171  
         {
 172  0
             throw new NestedRuntimeException( e );
 173  994
         }
 174  994
     }
 175  
 
 176  
     public void consumeMultiLineContent( String s )
 177  
         throws IOException
 178  
     {
 179  15
         BufferedReader stringReader = new BufferedReader( new StringReader( s ) );
 180  
         String s1;
 181  1009
         while ( ( s1 = stringReader.readLine() ) != null )
 182  
         {
 183  994
             consumeLine( s1 );
 184  
         }
 185  15
     }
 186  
 
 187  
     private String createConsoleMessage( String remaining )
 188  
     {
 189  1
         return unescape( remaining );
 190  
     }
 191  
 
 192  
     private ReportEntry createReportEntry( String untokenized )
 193  
     {
 194  16
         StringTokenizer tokens = new StringTokenizer( untokenized, "," );
 195  
         try
 196  
         {
 197  16
             String source = nullableCsv( tokens.nextToken() );
 198  16
             String name = nullableCsv( tokens.nextToken() );
 199  16
             String group = nullableCsv( tokens.nextToken() );
 200  16
             String message = nullableCsv( tokens.nextToken() );
 201  16
             String elapsedStr = tokens.nextToken();
 202  16
             Integer elapsed = "null".equals( elapsedStr ) ? null : Integer.decode( elapsedStr );
 203  16
             final StackTraceWriter stackTraceWriter =
 204  
                 tokens.hasMoreTokens() ? deserializeStackStraceWriter( tokens ) : null;
 205  
 
 206  16
             return CategorizedReportEntry.reportEntry( source, name, group, stackTraceWriter, elapsed, message );
 207  
         }
 208  0
         catch ( RuntimeException e )
 209  
         {
 210  0
             throw new RuntimeException( untokenized, e );
 211  
         }
 212  
     }
 213  
 
 214  
     private StackTraceWriter deserializeStackStraceWriter( StringTokenizer tokens )
 215  
     {
 216  
         StackTraceWriter stackTraceWriter;
 217  3
         String stackTraceMessage = nullableCsv( tokens.nextToken() );
 218  3
         String smartStackTrace = nullableCsv( tokens.nextToken() );
 219  3
         String stackTrace = tokens.hasMoreTokens() ? nullableCsv( tokens.nextToken() ) : null;
 220  3
         stackTraceWriter =
 221  
             stackTrace != null ? new DeserializedStacktraceWriter( stackTraceMessage, smartStackTrace, stackTrace )
 222  
                             : null;
 223  3
         return stackTraceWriter;
 224  
     }
 225  
 
 226  
     private String nullableCsv( String source )
 227  
     {
 228  73
         if ( "null".equals( source ) )
 229  
         {
 230  28
             return null;
 231  
         }
 232  45
         return unescape( source );
 233  
     }
 234  
 
 235  
     private String unescape( String source )
 236  
     {
 237  46
         StringBuilder stringBuffer = new StringBuilder( source.length() );
 238  
 
 239  46
         StringUtils.unescapeString( stringBuffer, source );
 240  46
         return stringBuffer.toString();
 241  
     }
 242  
 
 243  
     /**
 244  
      * Used when getting reporters on the plugin side of a fork.
 245  
      *
 246  
      * @param channelNumber The logical channel number
 247  
      * @return A mock provider reporter
 248  
      */
 249  
     public RunListener getReporter( Integer channelNumber )
 250  
     {
 251  15
         return testSetReporters.get( channelNumber );
 252  
     }
 253  
 
 254  
     private RunListener getOrCreateReporter( Integer channelNumber )
 255  
     {
 256  18
         RunListener reporter = testSetReporters.get( channelNumber );
 257  18
         if ( reporter == null )
 258  
         {
 259  15
             reporter = defaultReporterFactory.createReporter();
 260  15
             testSetReporters.put( channelNumber, reporter );
 261  
         }
 262  18
         return reporter;
 263  
     }
 264  
 
 265  
     private ConsoleOutputReceiver getOrCreateConsoleOutputReceiver( Integer channelNumber )
 266  
     {
 267  1
         return (ConsoleOutputReceiver) getOrCreateReporter( channelNumber );
 268  
     }
 269  
 
 270  
     private ConsoleLogger getOrCreateConsoleLogger( Integer channelNumber )
 271  
     {
 272  1
         return (ConsoleLogger) getOrCreateReporter( channelNumber );
 273  
     }
 274  
 
 275  
     public void close( boolean hadTimeout )
 276  
     {
 277  0
     }
 278  
 
 279  
     public boolean isSaidGoodBye()
 280  
     {
 281  0
         return saidGoodBye;
 282  
     }
 283  
 
 284  
     public StackTraceWriter getErrorInFork()
 285  
     {
 286  0
         return errorInFork;
 287  
     }
 288  
 
 289  
     public boolean isErrorInFork()
 290  
     {
 291  0
         return errorInFork != null;
 292  
     }
 293  
 }