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.ByteArrayInputStream;
22  import java.io.ByteArrayOutputStream;
23  import java.io.File;
24  import java.io.FileInputStream;
25  import java.io.IOException;
26  import java.io.InputStream;
27  import java.io.InterruptedIOException;
28  import java.io.OutputStream;
29  import java.nio.Buffer;
30  import java.nio.ByteBuffer;
31  import java.nio.channels.AsynchronousByteChannel;
32  import java.nio.channels.ClosedChannelException;
33  import java.nio.channels.NonReadableChannelException;
34  import java.nio.channels.ReadableByteChannel;
35  import java.nio.channels.ShutdownChannelGroupException;
36  import java.util.concurrent.ExecutionException;
37  import java.util.concurrent.Future;
38  
39  import org.junit.Rule;
40  import org.junit.Test;
41  import org.junit.rules.ExpectedException;
42  import org.junit.rules.TemporaryFolder;
43  import org.mockito.ArgumentCaptor;
44  import org.mockito.invocation.InvocationOnMock;
45  import org.mockito.stubbing.Answer;
46  
47  import static java.nio.file.Files.write;
48  import static org.assertj.core.api.Assertions.assertThat;
49  import static org.hamcrest.Matchers.instanceOf;
50  import static org.mockito.ArgumentMatchers.any;
51  import static org.mockito.Mockito.doThrow;
52  import static org.mockito.Mockito.mock;
53  import static org.mockito.Mockito.never;
54  import static org.mockito.Mockito.times;
55  import static org.mockito.Mockito.verify;
56  import static org.mockito.Mockito.verifyNoMoreInteractions;
57  import static org.mockito.Mockito.verifyZeroInteractions;
58  import static org.mockito.Mockito.when;
59  import static org.powermock.reflect.Whitebox.invokeMethod;
60  
61  /**
62   * The tests for {@link Channels#newChannel(InputStream)} and {@link Channels#newBufferedChannel(InputStream)}.
63   */
64  public class ChannelsReaderTest {
65      @Rule
66      public final ExpectedException ee = ExpectedException.none();
67  
68      @Rule
69      public final TemporaryFolder tmp =
70              TemporaryFolder.builder().assureDeletion().build();
71  
72      @Test
73      public void shouldOverflowBuffer() throws Exception {
74          ByteArrayOutputStream out = new ByteArrayOutputStream();
75  
76          WritableBufferedByteChannel channel = invokeMethod(
77                  Channels.class, "newChannel", new Class[] {OutputStream.class, int.class}, new Object[] {out, 8});
78  
79          assertThat(channel.countBufferOverflows()).isEqualTo(0);
80  
81          channel.write(ByteBuffer.wrap(new byte[] {1, 2, 3}));
82  
83          assertThat(channel.countBufferOverflows()).isEqualTo(0);
84  
85          channel.write(ByteBuffer.wrap(new byte[] {4, 5, 6, 7, 8}));
86  
87          assertThat(channel.countBufferOverflows()).isEqualTo(0);
88  
89          channel.write(ByteBuffer.wrap(new byte[] {9}));
90  
91          assertThat(channel.countBufferOverflows()).isEqualTo(1);
92  
93          assertThat(out.toByteArray()).isEqualTo(new byte[] {1, 2, 3, 4, 5, 6, 7, 8, 9});
94      }
95  
96      @Test
97      public void exactBufferSize() throws Exception {
98          ByteArrayInputStream is = new ByteArrayInputStream(new byte[] {1, 2, 3});
99          ReadableByteChannel channel = Channels.newChannel(is);
100         ByteBuffer bb = ByteBuffer.allocate(3);
101 
102         int countWritten = channel.read(bb);
103 
104         assertThat(countWritten).isEqualTo(3);
105 
106         assertThat(bb.arrayOffset()).isEqualTo(0);
107 
108         assertThat(((Buffer) bb).position()).isEqualTo(3);
109 
110         assertThat(bb.remaining()).isEqualTo(0);
111 
112         assertThat(((Buffer) bb).limit()).isEqualTo(3);
113 
114         assertThat(bb.capacity()).isEqualTo(3);
115 
116         ((Buffer) bb).flip();
117 
118         assertThat(bb.arrayOffset()).isEqualTo(0);
119 
120         assertThat(((Buffer) bb).position()).isEqualTo(0);
121 
122         assertThat(bb.remaining()).isEqualTo(3);
123 
124         assertThat(((Buffer) bb).limit()).isEqualTo(3);
125 
126         assertThat(bb.capacity()).isEqualTo(3);
127 
128         assertThat(bb.array()).isEqualTo(new byte[] {1, 2, 3});
129 
130         assertThat(channel.isOpen()).isTrue();
131 
132         channel.close();
133 
134         assertThat(channel.isOpen()).isFalse();
135     }
136 
137     @Test
138     public void bufferedChannel() throws Exception {
139         ByteArrayInputStream is = new ByteArrayInputStream(new byte[] {1, 2, 3});
140         ReadableByteChannel channel = Channels.newBufferedChannel(is);
141         ByteBuffer bb = ByteBuffer.allocate(4);
142 
143         int countWritten = channel.read(bb);
144 
145         assertThat(countWritten).isEqualTo(3);
146 
147         assertThat(bb.arrayOffset()).isEqualTo(0);
148 
149         assertThat(((Buffer) bb).position()).isEqualTo(3);
150 
151         assertThat(bb.remaining()).isEqualTo(1);
152 
153         assertThat(((Buffer) bb).limit()).isEqualTo(4);
154 
155         assertThat(bb.capacity()).isEqualTo(4);
156 
157         ((Buffer) bb).flip();
158 
159         assertThat(bb.arrayOffset()).isEqualTo(0);
160 
161         assertThat(((Buffer) bb).position()).isEqualTo(0);
162 
163         assertThat(bb.remaining()).isEqualTo(3);
164 
165         assertThat(((Buffer) bb).limit()).isEqualTo(3);
166 
167         assertThat(bb.capacity()).isEqualTo(4);
168 
169         assertThat(bb.array()).isEqualTo(new byte[] {1, 2, 3, 0});
170 
171         assertThat(channel.isOpen()).isTrue();
172 
173         channel.close();
174 
175         assertThat(channel.isOpen()).isFalse();
176     }
177 
178     @Test
179     public void biggerBuffer() throws Exception {
180         ByteArrayInputStream is = new ByteArrayInputStream(new byte[] {1, 2, 3});
181         ReadableByteChannel channel = Channels.newChannel(is);
182         ByteBuffer bb = ByteBuffer.allocate(4);
183 
184         int countWritten = channel.read(bb);
185 
186         assertThat(countWritten).isEqualTo(3);
187 
188         assertThat(bb.arrayOffset()).isEqualTo(0);
189 
190         assertThat(((Buffer) bb).position()).isEqualTo(3);
191 
192         assertThat(bb.remaining()).isEqualTo(1);
193 
194         assertThat(((Buffer) bb).limit()).isEqualTo(4);
195 
196         assertThat(bb.capacity()).isEqualTo(4);
197 
198         ((Buffer) bb).flip();
199 
200         assertThat(bb.arrayOffset()).isEqualTo(0);
201 
202         assertThat(((Buffer) bb).position()).isEqualTo(0);
203 
204         assertThat(bb.remaining()).isEqualTo(3);
205 
206         assertThat(((Buffer) bb).limit()).isEqualTo(3);
207 
208         assertThat(bb.capacity()).isEqualTo(4);
209 
210         assertThat(bb.array()).isEqualTo(new byte[] {1, 2, 3, 0});
211 
212         assertThat(channel.isOpen()).isTrue();
213 
214         channel.close();
215 
216         assertThat(channel.isOpen()).isFalse();
217     }
218 
219     @Test
220     public void shouldFailAfterClosed() throws IOException {
221         ByteArrayInputStream is = new ByteArrayInputStream(new byte[] {1, 2, 3});
222         ReadableByteChannel channel = Channels.newChannel(is);
223         channel.close();
224         assertThat(channel.isOpen()).isFalse();
225         ee.expect(ClosedChannelException.class);
226         channel.read(ByteBuffer.allocate(0));
227     }
228 
229     @Test
230     public void shouldFailIfNotReadable() throws IOException {
231         ByteArrayInputStream is = new ByteArrayInputStream(new byte[] {1, 2, 3});
232         ReadableByteChannel channel = Channels.newChannel(is);
233         ee.expect(NonReadableChannelException.class);
234         channel.read(ByteBuffer.allocate(0).asReadOnlyBuffer());
235     }
236 
237     @Test
238     public void shouldFailIOnDirectBuffer() throws IOException {
239         ByteArrayInputStream is = new ByteArrayInputStream(new byte[] {1, 2, 3});
240         ReadableByteChannel channel = Channels.newChannel(is);
241         ee.expect(NonReadableChannelException.class);
242         channel.read(ByteBuffer.allocateDirect(0));
243     }
244 
245     @Test
246     public void shouldUseFileChannel() throws IOException {
247         File f = tmp.newFile();
248         write(f.toPath(), new byte[] {1, 2, 3});
249         FileInputStream is = new FileInputStream(f);
250         ReadableByteChannel channel = Channels.newChannel(is);
251         ByteBuffer bb = ByteBuffer.allocate(4);
252         int countWritten = channel.read(bb);
253 
254         assertThat(channel.isOpen()).isTrue();
255 
256         channel.close();
257 
258         assertThat(channel.isOpen()).isFalse();
259 
260         assertThat(countWritten).isEqualTo(3);
261 
262         assertThat(bb.arrayOffset()).isEqualTo(0);
263 
264         assertThat(((Buffer) bb).position()).isEqualTo(3);
265 
266         assertThat(bb.remaining()).isEqualTo(1);
267 
268         assertThat(((Buffer) bb).limit()).isEqualTo(4);
269 
270         assertThat(bb.capacity()).isEqualTo(4);
271 
272         ((Buffer) bb).flip();
273 
274         assertThat(bb.arrayOffset()).isEqualTo(0);
275 
276         assertThat(((Buffer) bb).position()).isEqualTo(0);
277 
278         assertThat(bb.remaining()).isEqualTo(3);
279 
280         assertThat(((Buffer) bb).limit()).isEqualTo(3);
281 
282         assertThat(bb.capacity()).isEqualTo(4);
283 
284         assertThat(bb.array()).isEqualTo(new byte[] {1, 2, 3, 0});
285     }
286 
287     @Test(expected = IndexOutOfBoundsException.class)
288     public void shouldValidateInput1() throws Exception {
289         AsynchronousByteChannel channel = mock(AsynchronousByteChannel.class);
290         InputStream is = Channels.newInputStream(channel);
291         is.read(new byte[0], -1, 0);
292     }
293 
294     @Test(expected = IndexOutOfBoundsException.class)
295     public void shouldValidateInput2() throws Exception {
296         AsynchronousByteChannel channel = mock(AsynchronousByteChannel.class);
297         InputStream is = Channels.newInputStream(channel);
298         is.read(new byte[0], 0, -1);
299     }
300 
301     @Test(expected = IndexOutOfBoundsException.class)
302     public void shouldValidateInput3() throws Exception {
303         AsynchronousByteChannel channel = mock(AsynchronousByteChannel.class);
304         InputStream is = Channels.newInputStream(channel);
305         is.read(new byte[0], 1, 0);
306     }
307 
308     @Test(expected = IndexOutOfBoundsException.class)
309     public void shouldValidateInput4() throws Exception {
310         AsynchronousByteChannel channel = mock(AsynchronousByteChannel.class);
311         InputStream is = Channels.newInputStream(channel);
312         is.read(new byte[0], 0, 1);
313     }
314 
315     @Test
316     public void shouldClose() throws Exception {
317         AsynchronousByteChannel channel = mock(AsynchronousByteChannel.class);
318         when(channel.isOpen()).thenReturn(true);
319         InputStream is = Channels.newInputStream(channel);
320         is.close();
321         verify(channel, times(1)).close();
322     }
323 
324     @Test
325     public void shouldNotClose() throws Exception {
326         AsynchronousByteChannel channel = mock(AsynchronousByteChannel.class);
327         when(channel.isOpen()).thenReturn(false);
328         InputStream is = Channels.newInputStream(channel);
329         is.close();
330         verify(channel, never()).close();
331     }
332 
333     @Test
334     public void shouldAlreadyClosed() throws Exception {
335         AsynchronousByteChannel channel = mock(AsynchronousByteChannel.class);
336         when(channel.isOpen()).thenReturn(true);
337         doThrow(ClosedChannelException.class).when(channel).close();
338         InputStream is = Channels.newInputStream(channel);
339         is.close();
340         verify(channel).close();
341     }
342 
343     @Test
344     public void shouldReadZeroLength() throws Exception {
345         AsynchronousByteChannel channel = mock(AsynchronousByteChannel.class);
346         InputStream is = Channels.newInputStream(channel);
347         is.read(new byte[] {5}, 0, 0);
348         verifyZeroInteractions(channel);
349     }
350 
351     @Test
352     public void shouldReadArray() throws Exception {
353         AsynchronousByteChannel channel = mock(AsynchronousByteChannel.class);
354         when(channel.read(any(ByteBuffer.class))).thenAnswer(new Answer<Future<Integer>>() {
355             @Override
356             public Future<Integer> answer(InvocationOnMock invocation) throws Throwable {
357                 ByteBuffer bb = (ByteBuffer) invocation.getArguments()[0];
358                 bb.put((byte) 3);
359                 bb.put((byte) 4);
360                 Future<Integer> future = mock(Future.class);
361                 when(future.get()).thenReturn(2);
362                 return future;
363             }
364         });
365 
366         InputStream is = Channels.newInputStream(channel);
367         ArgumentCaptor<ByteBuffer> captured = ArgumentCaptor.forClass(ByteBuffer.class);
368         byte[] b = new byte[] {1, 2, 0, 0, 5};
369         is.read(b, 2, 2);
370 
371         verify(channel).read(captured.capture());
372         verifyNoMoreInteractions(channel);
373 
374         assertThat(captured.getAllValues()).hasSize(1);
375 
376         assertThat(captured.getAllValues().get(0).array()).containsOnly(new byte[] {1, 2, 3, 4, 5});
377 
378         assertThat(captured.getAllValues().get(0).arrayOffset()).isEqualTo(0);
379 
380         assertThat(captured.getAllValues().get(0).position()).isEqualTo(4);
381 
382         assertThat(captured.getAllValues().get(0).limit()).isEqualTo(4);
383 
384         assertThat(captured.getAllValues().get(0).capacity()).isEqualTo(5);
385     }
386 
387     @Test
388     public void shouldRead() throws Exception {
389         AsynchronousByteChannel channel = mock(AsynchronousByteChannel.class);
390         when(channel.read(any(ByteBuffer.class))).thenAnswer(new Answer<Future<Integer>>() {
391             @Override
392             public Future<Integer> answer(InvocationOnMock invocation) throws Throwable {
393                 ByteBuffer bb = (ByteBuffer) invocation.getArguments()[0];
394                 bb.put((byte) 3);
395                 Future<Integer> future = mock(Future.class);
396                 when(future.get()).thenReturn(1);
397                 return future;
398             }
399         });
400 
401         InputStream is = Channels.newInputStream(channel);
402         ArgumentCaptor<ByteBuffer> captured = ArgumentCaptor.forClass(ByteBuffer.class);
403         int b = is.read();
404         assertThat(b).isEqualTo(3);
405 
406         verify(channel).read(captured.capture());
407         verifyNoMoreInteractions(channel);
408 
409         assertThat(captured.getAllValues()).hasSize(1);
410 
411         assertThat(captured.getAllValues().get(0).array()).containsOnly(new byte[] {3});
412 
413         assertThat(captured.getAllValues().get(0).arrayOffset()).isEqualTo(0);
414 
415         assertThat(captured.getAllValues().get(0).position()).isEqualTo(1);
416 
417         assertThat(captured.getAllValues().get(0).limit()).isEqualTo(1);
418 
419         assertThat(captured.getAllValues().get(0).capacity()).isEqualTo(1);
420     }
421 
422     @Test
423     public void shouldThrowExceptionOnRead() throws Exception {
424         AsynchronousByteChannel channel = mock(AsynchronousByteChannel.class);
425         when(channel.read(any(ByteBuffer.class))).thenThrow(ShutdownChannelGroupException.class);
426         InputStream is = Channels.newInputStream(channel);
427         ee.expect(IOException.class);
428         ee.expectCause(instanceOf(ShutdownChannelGroupException.class));
429         is.read(new byte[1], 0, 1);
430     }
431 
432     @Test
433     public void shouldThrowExceptionOnFuture1() throws Exception {
434         AsynchronousByteChannel channel = mock(AsynchronousByteChannel.class);
435         Future<Integer> future = mock(Future.class);
436         when(future.get()).thenThrow(new ExecutionException(new InterruptedIOException()));
437         when(channel.read(any(ByteBuffer.class))).thenReturn(future);
438         InputStream is = Channels.newInputStream(channel);
439         ee.expect(InterruptedIOException.class);
440         is.read(new byte[1], 0, 1);
441     }
442 
443     @Test
444     public void shouldThrowExceptionOnFuture2() throws Exception {
445         AsynchronousByteChannel channel = mock(AsynchronousByteChannel.class);
446         Future<Integer> future = mock(Future.class);
447         when(future.get()).thenThrow(new ExecutionException(new RuntimeException("msg")));
448         when(channel.read(any(ByteBuffer.class))).thenReturn(future);
449         InputStream is = Channels.newInputStream(channel);
450         ee.expect(IOException.class);
451         ee.expectCause(instanceOf(RuntimeException.class));
452         ee.expectMessage("msg");
453         is.read(new byte[1], 0, 1);
454     }
455 
456     @Test
457     public void shouldThrowExceptionOnFuture3() throws Exception {
458         AsynchronousByteChannel channel = mock(AsynchronousByteChannel.class);
459         Future<Integer> future = mock(Future.class);
460         when(future.get()).thenThrow(new ExecutionException("msg", null));
461         when(channel.read(any(ByteBuffer.class))).thenReturn(future);
462         InputStream is = Channels.newInputStream(channel);
463         ee.expect(IOException.class);
464         ee.expectMessage("msg");
465         is.read(new byte[1], 0, 1);
466     }
467 }