View Javadoc
1   package org.apache.maven.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.booterclient.MockReporter;
23  import org.apache.maven.plugin.surefire.booterclient.lazytestprovider.TestLessInputStream;
24  import org.apache.maven.plugin.surefire.booterclient.lazytestprovider.TestLessInputStream.TestLessInputStreamBuilder;
25  import org.apache.maven.plugin.surefire.extensions.SurefireForkNodeFactory;
26  import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
27  import org.apache.maven.surefire.api.event.ControlByeEvent;
28  import org.apache.maven.surefire.api.event.Event;
29  import org.apache.maven.surefire.extensions.util.CountdownCloseable;
30  import org.junit.Test;
31  
32  import javax.annotation.Nonnull;
33  import java.io.Closeable;
34  import java.io.File;
35  import java.io.IOException;
36  import java.net.InetAddress;
37  import java.net.Socket;
38  import java.net.URI;
39  import java.nio.ByteBuffer;
40  import java.nio.channels.ReadableByteChannel;
41  import java.util.Queue;
42  import java.util.concurrent.ConcurrentLinkedQueue;
43  import java.util.concurrent.CountDownLatch;
44  import java.util.concurrent.atomic.AtomicBoolean;
45  
46  import static java.nio.charset.StandardCharsets.US_ASCII;
47  import static java.util.concurrent.TimeUnit.MILLISECONDS;
48  import static org.fest.assertions.Assertions.assertThat;
49  import static org.mockito.ArgumentMatchers.any;
50  import static org.mockito.Mockito.mock;
51  import static org.mockito.Mockito.when;
52  
53  /**
54   *
55   */
56  public class ForkChannelTest
57  {
58      private static final long TESTCASE_TIMEOUT = 30_000L;
59  
60      private final AtomicBoolean hasError = new AtomicBoolean();
61  
62      @Test( timeout = TESTCASE_TIMEOUT )
63      public void shouldRequestReplyMessagesViaTCP() throws Exception
64      {
65          ForkNodeArguments forkNodeArguments = new ForkNodeArguments()
66          {
67              @Override
68              public int getForkChannelId()
69              {
70                  return 1;
71              }
72  
73              @Override
74              @Nonnull
75              public File dumpStreamText( @Nonnull String text )
76              {
77                  return new File( "" );
78              }
79  
80              @Override
81              public void logWarningAtEnd( @Nonnull String text )
82              {
83              }
84  
85              @Override
86              @Nonnull
87              public ConsoleLogger getConsoleLogger()
88              {
89                  return new MockReporter();
90              }
91          };
92  
93          ForkNodeFactory factory = new SurefireForkNodeFactory();
94          try ( ForkChannel channel = factory.createForkChannel( forkNodeArguments ) )
95          {
96              assertThat( channel.getArguments().getForkChannelId() )
97                  .isEqualTo( 1 );
98  
99              assertThat( channel.getCountdownCloseablePermits() )
100                 .isEqualTo( 3 );
101 
102             String localHost = InetAddress.getLocalHost().getHostAddress();
103             assertThat( channel.getForkNodeConnectionString() )
104                 .startsWith( "tcp://" + localHost + ":" )
105                 .isNotEqualTo( "tcp://" + localHost + ":" );
106 
107             URI uri = new URI( channel.getForkNodeConnectionString() );
108 
109             assertThat( uri.getPort() )
110                 .isPositive();
111 
112             final TestLessInputStreamBuilder builder = new TestLessInputStreamBuilder();
113             TestLessInputStream commandReader = builder.build();
114             final CountDownLatch isCloseableCalled = new CountDownLatch( 1 );
115             Closeable closeable = new Closeable()
116             {
117                 @Override
118                 public void close()
119                 {
120                     isCloseableCalled.countDown();
121                 }
122             };
123             CountdownCloseable cc = new CountdownCloseable( closeable, 2 );
124             Consumer consumer = new Consumer();
125 
126             Client client = new Client( uri.getPort() );
127             client.start();
128 
129             channel.connectToClient();
130             channel.bindCommandReader( commandReader, null ).start();
131             ReadableByteChannel stdOut = mock( ReadableByteChannel.class );
132             when( stdOut.read( any( ByteBuffer.class ) ) ).thenReturn( -1 );
133             channel.bindEventHandler( consumer, cc, stdOut ).start();
134 
135             commandReader.noop();
136 
137             client.join( TESTCASE_TIMEOUT );
138 
139             assertThat( hasError.get() )
140                 .isFalse();
141 
142             assertThat( isCloseableCalled.await( TESTCASE_TIMEOUT, MILLISECONDS ) )
143                 .isTrue();
144 
145             assertThat( consumer.lines )
146                 .hasSize( 1 );
147 
148             assertThat( consumer.lines.element() )
149                 .isInstanceOf( ControlByeEvent.class );
150         }
151     }
152 
153     private static class Consumer implements EventHandler<Event>
154     {
155         final Queue<Event> lines = new ConcurrentLinkedQueue<>();
156 
157         @Override
158         public void handleEvent( @Nonnull Event s )
159         {
160             lines.add( s );
161         }
162     }
163 
164     private final class Client extends Thread
165     {
166         private final int port;
167 
168         private Client( int port )
169         {
170             this.port = port;
171         }
172 
173         @Override
174         public void run()
175         {
176             try ( Socket socket = new Socket( InetAddress.getLocalHost().getHostAddress(), port ) )
177             {
178                 byte[] data = new byte[128];
179                 int readLength = socket.getInputStream().read( data );
180                 String token = new String( data, 0, readLength, US_ASCII );
181                 assertThat( token ).isEqualTo( ":maven-surefire-command:noop:" );
182                 socket.getOutputStream().write( ":maven-surefire-event:bye:".getBytes( US_ASCII ) );
183             }
184             catch ( IOException e )
185             {
186                 hasError.set( true );
187                 e.printStackTrace();
188                 throw new IllegalStateException( e );
189             }
190             catch ( RuntimeException e )
191             {
192                 hasError.set( true );
193                 e.printStackTrace();
194                 throw e;
195             }
196         }
197     }
198 }