1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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
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 }