View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.maven.surefire.api.util.internal;
20  
21  import java.io.ByteArrayOutputStream;
22  import java.io.File;
23  import java.io.FileOutputStream;
24  import java.io.IOException;
25  import java.io.InterruptedIOException;
26  import java.io.OutputStream;
27  import java.nio.Buffer;
28  import java.nio.ByteBuffer;
29  import java.nio.channels.AsynchronousByteChannel;
30  import java.nio.channels.ClosedChannelException;
31  import java.nio.channels.NonWritableChannelException;
32  import java.nio.channels.ShutdownChannelGroupException;
33  import java.nio.channels.WritableByteChannel;
34  import java.util.concurrent.ExecutionException;
35  import java.util.concurrent.Future;
36  
37  import org.junit.Rule;
38  import org.junit.Test;
39  import org.junit.rules.ExpectedException;
40  import org.junit.rules.TemporaryFolder;
41  import org.mockito.ArgumentCaptor;
42  import org.mockito.invocation.InvocationOnMock;
43  import org.mockito.stubbing.Answer;
44  
45  import static java.nio.file.Files.readAllBytes;
46  import static org.assertj.core.api.Assertions.assertThat;
47  import static org.hamcrest.Matchers.instanceOf;
48  import static org.mockito.ArgumentMatchers.any;
49  import static org.mockito.Mockito.doThrow;
50  import static org.mockito.Mockito.mock;
51  import static org.mockito.Mockito.never;
52  import static org.mockito.Mockito.times;
53  import static org.mockito.Mockito.verify;
54  import static org.mockito.Mockito.verifyNoMoreInteractions;
55  import static org.mockito.Mockito.verifyZeroInteractions;
56  import static org.mockito.Mockito.when;
57  
58  /**
59   * The tests for {@link Channels#newChannel(OutputStream)} and {@link Channels#newBufferedChannel(OutputStream)}.
60   */
61  public class ChannelsWriterTest {
62      @Rule
63      public final ExpectedException ee = ExpectedException.none();
64  
65      @Rule
66      public final TemporaryFolder tmp =
67              TemporaryFolder.builder().assureDeletion().build();
68  
69      @Test
70      public void wrappedBuffer() throws Exception {
71          final boolean[] isFlush = {false};
72          ByteArrayOutputStream out = new ByteArrayOutputStream() {
73              @Override
74              public void flush() throws IOException {
75                  isFlush[0] = true;
76                  super.flush();
77              }
78          };
79          WritableByteChannel channel = Channels.newBufferedChannel(out);
80          ByteBuffer bb = ByteBuffer.wrap(new byte[] {1, 2, 3});
81          int countWritten = channel.write(bb);
82          assertThat(countWritten).isEqualTo(3);
83  
84          assertThat(out.toByteArray()).hasSize(3).isEqualTo(new byte[] {1, 2, 3});
85  
86          assertThat(isFlush).hasSize(1).containsOnly(true);
87  
88          assertThat(((Buffer) bb).position()).isEqualTo(3);
89  
90          assertThat(((Buffer) bb).limit()).isEqualTo(3);
91  
92          assertThat(bb.capacity()).isEqualTo(3);
93  
94          assertThat(channel.isOpen()).isTrue();
95      }
96  
97      @Test
98      public void bigBuffer() throws Exception {
99          ByteArrayOutputStream out = new ByteArrayOutputStream();
100         WritableByteChannel channel = Channels.newChannel(out);
101         ByteBuffer bb = ByteBuffer.allocate(4);
102         bb.put((byte) 1);
103         bb.put((byte) 2);
104         bb.put((byte) 3);
105         int countWritten = channel.write(bb);
106         assertThat(countWritten).isEqualTo(3);
107         assertThat(out.toByteArray()).hasSize(3).isEqualTo(new byte[] {1, 2, 3});
108 
109         assertThat(((Buffer) bb).position()).isEqualTo(3);
110 
111         assertThat(((Buffer) bb).limit()).isEqualTo(3);
112 
113         assertThat(bb.capacity()).isEqualTo(4);
114 
115         assertThat(channel.isOpen()).isTrue();
116     }
117 
118     @Test
119     public void shouldFlushWhenEmptyBuffer() throws Exception {
120         final boolean[] flushed = {false};
121         ByteArrayOutputStream out = new ByteArrayOutputStream() {
122             @Override
123             public void flush() throws IOException {
124                 flushed[0] = true;
125                 super.flush();
126             }
127         };
128         WritableByteChannel channel = Channels.newChannel(out);
129         ByteBuffer bb = ByteBuffer.allocate(0);
130         int countWritten = channel.write(bb);
131         assertThat(countWritten).isEqualTo(0);
132         assertThat(flushed[0]).isTrue();
133     }
134 
135     @Test
136     public void shouldFlushWhenEmptyBufferOnBufferedWrites() throws Exception {
137         final boolean[] flushed = {false};
138         ByteArrayOutputStream out = new ByteArrayOutputStream() {
139             @Override
140             public void flush() throws IOException {
141                 flushed[0] = true;
142                 super.flush();
143             }
144         };
145         WritableBufferedByteChannel channel = Channels.newBufferedChannel(out);
146         ByteBuffer bb = ByteBuffer.allocate(0);
147         channel.writeBuffered(bb);
148         assertThat(flushed[0]).isFalse();
149     }
150 
151     @Test
152     public void bufferedChannel() throws Exception {
153         ByteArrayOutputStream out = new ByteArrayOutputStream();
154         WritableBufferedByteChannel channel = Channels.newBufferedChannel(out);
155         ByteBuffer bb = ByteBuffer.allocate(5);
156         bb.put((byte) 1);
157         bb.put((byte) 2);
158         bb.put((byte) 3);
159 
160         channel.writeBuffered(bb);
161 
162         assertThat(out.toByteArray()).isEmpty();
163 
164         channel.write(ByteBuffer.wrap(new byte[] {4}));
165 
166         assertThat(out.toByteArray()).hasSize(4).isEqualTo(new byte[] {1, 2, 3, 4});
167 
168         assertThat(((Buffer) bb).position()).isEqualTo(3);
169 
170         assertThat(((Buffer) bb).limit()).isEqualTo(3);
171 
172         assertThat(bb.capacity()).isEqualTo(5);
173 
174         assertThat(channel.isOpen()).isTrue();
175     }
176 
177     @Test
178     public void shouldFailAfterClosed() throws IOException {
179         ByteArrayOutputStream out = new ByteArrayOutputStream();
180         WritableByteChannel channel = Channels.newChannel(out);
181         channel.close();
182         assertThat(channel.isOpen()).isFalse();
183         ee.expect(ClosedChannelException.class);
184         channel.write(ByteBuffer.allocate(0));
185     }
186 
187     @Test
188     public void shouldFailIfNotReadable() throws IOException {
189         ByteArrayOutputStream out = new ByteArrayOutputStream();
190         WritableByteChannel channel = Channels.newChannel(out);
191         ee.expect(NonWritableChannelException.class);
192         channel.write(ByteBuffer.allocate(0).asReadOnlyBuffer());
193     }
194 
195     @Test
196     public void shouldFailIOnDirectBuffer() throws IOException {
197         ByteArrayOutputStream out = new ByteArrayOutputStream();
198         WritableByteChannel channel = Channels.newChannel(out);
199         ee.expect(NonWritableChannelException.class);
200         channel.write(ByteBuffer.allocateDirect(0));
201     }
202 
203     @Test
204     public void shouldUseFileChannel() throws IOException {
205         File f = tmp.newFile();
206         FileOutputStream os = new FileOutputStream(f);
207         WritableByteChannel channel = Channels.newChannel(os);
208         ByteBuffer bb = ByteBuffer.wrap(new byte[] {1, 2, 3});
209         channel.write(bb);
210 
211         assertThat(channel.isOpen()).isTrue();
212 
213         channel.close();
214 
215         assertThat(channel.isOpen()).isFalse();
216 
217         assertThat(readAllBytes(f.toPath())).hasSize(3).isEqualTo(new byte[] {1, 2, 3});
218     }
219 
220     @Test(expected = IndexOutOfBoundsException.class)
221     public void shouldValidateInput1() throws Exception {
222         AsynchronousByteChannel channel = mock(AsynchronousByteChannel.class);
223         OutputStream os = Channels.newOutputStream(channel);
224         os.write(new byte[0], -1, 0);
225     }
226 
227     @Test(expected = IndexOutOfBoundsException.class)
228     public void shouldValidateInput2() throws Exception {
229         AsynchronousByteChannel channel = mock(AsynchronousByteChannel.class);
230         OutputStream os = Channels.newOutputStream(channel);
231         os.write(new byte[0], 0, -1);
232     }
233 
234     @Test(expected = IndexOutOfBoundsException.class)
235     public void shouldValidateInput3() throws Exception {
236         AsynchronousByteChannel channel = mock(AsynchronousByteChannel.class);
237         OutputStream os = Channels.newOutputStream(channel);
238         os.write(new byte[0], 1, 0);
239     }
240 
241     @Test(expected = IndexOutOfBoundsException.class)
242     public void shouldValidateInput4() throws Exception {
243         AsynchronousByteChannel channel = mock(AsynchronousByteChannel.class);
244         OutputStream os = Channels.newOutputStream(channel);
245         os.write(new byte[0], 0, 1);
246     }
247 
248     @Test
249     public void shouldClose() throws Exception {
250         AsynchronousByteChannel channel = mock(AsynchronousByteChannel.class);
251         when(channel.isOpen()).thenReturn(true);
252         OutputStream os = Channels.newOutputStream(channel);
253         os.close();
254         verify(channel, times(1)).close();
255     }
256 
257     @Test
258     public void shouldNotClose() throws Exception {
259         AsynchronousByteChannel channel = mock(AsynchronousByteChannel.class);
260         when(channel.isOpen()).thenReturn(false);
261         OutputStream os = Channels.newOutputStream(channel);
262         os.close();
263         verify(channel, never()).close();
264     }
265 
266     @Test
267     public void shouldAlreadyClosed() throws Exception {
268         AsynchronousByteChannel channel = mock(AsynchronousByteChannel.class);
269         when(channel.isOpen()).thenReturn(true);
270         doThrow(ClosedChannelException.class).when(channel).close();
271         OutputStream os = Channels.newOutputStream(channel);
272         os.close();
273         verify(channel).close();
274     }
275 
276     @Test
277     public void shouldWriteZeroLength() throws Exception {
278         AsynchronousByteChannel channel = mock(AsynchronousByteChannel.class);
279         OutputStream os = Channels.newOutputStream(channel);
280         os.write(new byte[] {5}, 0, 0);
281         verifyZeroInteractions(channel);
282     }
283 
284     @Test
285     public void shouldWriteArray() throws Exception {
286         AsynchronousByteChannel channel = mock(AsynchronousByteChannel.class);
287         when(channel.write(any(ByteBuffer.class))).thenAnswer(new Answer<Future<Integer>>() {
288             @Override
289             public Future<Integer> answer(InvocationOnMock invocation) throws Throwable {
290                 ByteBuffer bb = (ByteBuffer) invocation.getArguments()[0];
291                 int i = 0;
292                 for (; bb.hasRemaining(); i++) {
293                     bb.get();
294                 }
295                 Future<Integer> future = mock(Future.class);
296                 when(future.get()).thenReturn(i);
297                 return future;
298             }
299         });
300 
301         OutputStream os = Channels.newOutputStream(channel);
302         ArgumentCaptor<ByteBuffer> captured = ArgumentCaptor.forClass(ByteBuffer.class);
303         os.write(new byte[] {1, 2, 3, 4, 5}, 2, 2);
304 
305         verify(channel).write(captured.capture());
306         verifyNoMoreInteractions(channel);
307 
308         assertThat(captured.getAllValues()).hasSize(1);
309 
310         assertThat(captured.getAllValues().get(0).array()).containsOnly(new byte[] {1, 2, 3, 4, 5});
311 
312         assertThat(captured.getAllValues().get(0).arrayOffset()).isEqualTo(0);
313 
314         assertThat(captured.getAllValues().get(0).position()).isEqualTo(4);
315 
316         assertThat(captured.getAllValues().get(0).limit()).isEqualTo(4);
317 
318         assertThat(captured.getAllValues().get(0).capacity()).isEqualTo(5);
319     }
320 
321     @Test
322     public void shouldWrite() throws Exception {
323         AsynchronousByteChannel channel = mock(AsynchronousByteChannel.class);
324         when(channel.write(any(ByteBuffer.class))).thenAnswer(new Answer<Future<Integer>>() {
325             @Override
326             public Future<Integer> answer(InvocationOnMock invocation) throws Throwable {
327                 ByteBuffer bb = (ByteBuffer) invocation.getArguments()[0];
328                 int i = 0;
329                 for (; bb.hasRemaining(); i++) {
330                     bb.get();
331                 }
332                 Future<Integer> future = mock(Future.class);
333                 when(future.get()).thenReturn(i);
334                 return future;
335             }
336         });
337 
338         OutputStream os = Channels.newOutputStream(channel);
339         ArgumentCaptor<ByteBuffer> captured = ArgumentCaptor.forClass(ByteBuffer.class);
340         os.write(3);
341 
342         verify(channel).write(captured.capture());
343         verifyNoMoreInteractions(channel);
344 
345         assertThat(captured.getAllValues()).hasSize(1);
346 
347         assertThat(captured.getAllValues().get(0).array()).containsOnly(new byte[] {3});
348 
349         assertThat(captured.getAllValues().get(0).arrayOffset()).isEqualTo(0);
350 
351         assertThat(captured.getAllValues().get(0).position()).isEqualTo(1);
352 
353         assertThat(captured.getAllValues().get(0).limit()).isEqualTo(1);
354 
355         assertThat(captured.getAllValues().get(0).capacity()).isEqualTo(1);
356     }
357 
358     @Test
359     public void shouldThrowExceptionOnWrite() throws Exception {
360         AsynchronousByteChannel channel = mock(AsynchronousByteChannel.class);
361         when(channel.write(any(ByteBuffer.class))).thenThrow(ShutdownChannelGroupException.class);
362         OutputStream os = Channels.newOutputStream(channel);
363         ee.expect(IOException.class);
364         ee.expectCause(instanceOf(ShutdownChannelGroupException.class));
365         os.write(new byte[1], 0, 1);
366     }
367 
368     @Test
369     public void shouldThrowExceptionOnFuture1() throws Exception {
370         AsynchronousByteChannel channel = mock(AsynchronousByteChannel.class);
371         Future<Integer> future = mock(Future.class);
372         when(future.get()).thenThrow(new ExecutionException(new InterruptedIOException()));
373         when(channel.write(any(ByteBuffer.class))).thenReturn(future);
374         OutputStream os = Channels.newOutputStream(channel);
375         ee.expect(InterruptedIOException.class);
376         os.write(new byte[1], 0, 1);
377     }
378 
379     @Test
380     public void shouldThrowExceptionOnFuture2() throws Exception {
381         AsynchronousByteChannel channel = mock(AsynchronousByteChannel.class);
382         Future<Integer> future = mock(Future.class);
383         when(future.get()).thenThrow(new ExecutionException(new RuntimeException("msg")));
384         when(channel.write(any(ByteBuffer.class))).thenReturn(future);
385         OutputStream os = Channels.newOutputStream(channel);
386         ee.expect(IOException.class);
387         ee.expectCause(instanceOf(RuntimeException.class));
388         ee.expectMessage("msg");
389         os.write(new byte[1], 0, 1);
390     }
391 
392     @Test
393     public void shouldThrowExceptionOnFuture3() throws Exception {
394         AsynchronousByteChannel channel = mock(AsynchronousByteChannel.class);
395         Future<Integer> future = mock(Future.class);
396         when(future.get()).thenThrow(new ExecutionException("msg", null));
397         when(channel.write(any(ByteBuffer.class))).thenReturn(future);
398         OutputStream os = Channels.newOutputStream(channel);
399         ee.expect(IOException.class);
400         ee.expectMessage("msg");
401         os.write(new byte[1], 0, 1);
402     }
403 }