View Javadoc
1   package org.apache.maven.plugin.surefire.extensions;
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 org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
23  import org.apache.maven.surefire.api.booter.MasterProcessChannelEncoder;
24  import org.apache.maven.surefire.booter.spi.SurefireMasterProcessChannelProcessorFactory;
25  import org.apache.maven.surefire.api.event.Event;
26  import org.apache.maven.surefire.extensions.EventHandler;
27  import org.apache.maven.surefire.extensions.ForkNodeArguments;
28  import org.apache.maven.surefire.extensions.util.CountdownCloseable;
29  import org.apache.maven.surefire.api.report.ConsoleOutputReceiver;
30  import org.junit.Test;
31  
32  import javax.annotation.Nonnull;
33  import java.io.Closeable;
34  import java.nio.ByteBuffer;
35  import java.nio.channels.ReadableByteChannel;
36  import java.util.concurrent.CountDownLatch;
37  import java.util.concurrent.TimeUnit;
38  import java.util.concurrent.atomic.AtomicInteger;
39  import java.util.concurrent.atomic.AtomicLong;
40  
41  import static org.fest.assertions.Assertions.assertThat;
42  import static org.mockito.ArgumentMatchers.any;
43  import static org.mockito.Mockito.mock;
44  import static org.mockito.Mockito.when;
45  
46  /**
47   * Simulates the End To End use case where Maven process and Surefire process communicate using the TCP/IP protocol.
48   */
49  @SuppressWarnings( "checkstyle:magicnumber" )
50  public class E2ETest
51  {
52      private static final String LONG_STRING =
53          "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789";
54  
55      @Test
56      public void test() throws Exception
57      {
58          ConsoleLogger logger = mock( ConsoleLogger.class );
59          ForkNodeArguments arguments = mock( ForkNodeArguments.class );
60          when( arguments.getForkChannelId() ).thenReturn( 1 );
61          when( arguments.getConsoleLogger() ).thenReturn( logger );
62          final SurefireForkChannel server = new SurefireForkChannel( arguments );
63  
64          final String connection = server.getForkNodeConnectionString();
65  
66          final SurefireMasterProcessChannelProcessorFactory factory = new SurefireMasterProcessChannelProcessorFactory();
67          factory.connect( connection );
68          final MasterProcessChannelEncoder encoder = factory.createEncoder();
69  
70          System.gc();
71  
72          TimeUnit.SECONDS.sleep( 3L );
73  
74          final CountDownLatch awaitHandlerFinished = new CountDownLatch( 2 );
75  
76          Thread t = new Thread()
77          {
78              @Override
79              public void run()
80              {
81                  ConsoleOutputReceiver target = new ConsoleOutputReceiver()
82                  {
83                      @Override
84                      public void writeTestOutput( String output, boolean newLine, boolean stdout )
85                      {
86                          encoder.stdOut( output, true );
87                      }
88                  };
89  
90                  //PrintStream out = System.out;
91                  //PrintStream err = System.err;
92  
93                  //ConsoleOutputCapture.startCapture( target );
94  
95                  try
96                  {
97                      long t1 = System.currentTimeMillis();
98                      for ( int i = 0; i < 400_000; i++ )
99                      {
100                         //System.out.println( LONG_STRING );
101                         encoder.stdOut( LONG_STRING, true );
102                     }
103                     long t2 = System.currentTimeMillis();
104                     long spent = t2 - t1;
105                     //System.setOut( out );
106                     //System.setErr( err );
107                     System.out.println( spent + "ms on write" );
108                     awaitHandlerFinished.countDown();
109                 }
110                 catch ( Exception e )
111                 {
112                     e.printStackTrace();
113                 }
114             }
115         };
116         t.setDaemon( true );
117         t.start();
118 
119         server.connectToClient();
120 
121         final AtomicLong readTime = new AtomicLong();
122 
123         EventHandler<Event> h = new EventHandler<Event>()
124         {
125             private final AtomicInteger counter = new AtomicInteger();
126             private volatile long t1;
127 
128             @Override
129             public void handleEvent( @Nonnull Event event )
130             {
131                 try
132                 {
133                     if ( counter.getAndIncrement() == 0 )
134                     {
135                         t1 = System.currentTimeMillis();
136                     }
137 
138                     long t2 = System.currentTimeMillis();
139                     long spent = t2 - t1;
140 
141                     if ( counter.get() % 100_000 == 0 )
142                     {
143                         System.out.println( spent + "ms: " + counter.get() );
144                     }
145 
146                     if ( counter.get() == 320_000 )
147                     {
148                         readTime.set( spent );
149                         System.out.println( spent + "ms on read" );
150                         awaitHandlerFinished.countDown();
151                     }
152                 }
153                 catch ( Exception e )
154                 {
155                     e.printStackTrace();
156                 }
157             }
158         };
159 
160         Closeable c = new Closeable()
161         {
162             @Override
163             public void close()
164             {
165             }
166         };
167 
168         ReadableByteChannel stdOut = mock( ReadableByteChannel.class );
169         when( stdOut.read( any( ByteBuffer.class ) ) ).thenReturn( -1 );
170         server.bindEventHandler( h, new CountdownCloseable( c, 1 ), stdOut )
171             .start();
172 
173         assertThat( awaitHandlerFinished.await( 30L, TimeUnit.SECONDS ) )
174             .isTrue();
175 
176         factory.close();
177         server.close();
178 
179         // 2 seconds while using the encoder/decoder
180         // 160 millis of sending pure data without encoder/decoder
181         assertThat( readTime.get() )
182             .describedAs( "The performance test should assert 2s of read time. "
183                 + "The limit 6s guarantees that the read time does not exceed this limit on overloaded CPU." )
184             .isPositive()
185             .isLessThanOrEqualTo( 6_000L );
186     }
187 }