1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.commons.io.channels;
19
20 import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
21 import static org.junit.jupiter.api.Assertions.assertEquals;
22 import static org.junit.jupiter.api.Assertions.assertFalse;
23 import static org.junit.jupiter.api.Assertions.assertInstanceOf;
24 import static org.junit.jupiter.api.Assertions.assertNotSame;
25 import static org.junit.jupiter.api.Assertions.assertSame;
26 import static org.junit.jupiter.api.Assertions.assertThrows;
27 import static org.junit.jupiter.api.Assertions.assertTrue;
28 import static org.mockito.Mockito.mock;
29 import static org.mockito.Mockito.never;
30 import static org.mockito.Mockito.times;
31 import static org.mockito.Mockito.verify;
32 import static org.mockito.Mockito.verifyNoInteractions;
33 import static org.mockito.Mockito.verifyNoMoreInteractions;
34 import static org.mockito.Mockito.when;
35
36 import java.io.IOException;
37 import java.nio.channels.AsynchronousChannel;
38 import java.nio.channels.ByteChannel;
39 import java.nio.channels.Channel;
40 import java.nio.channels.ClosedChannelException;
41 import java.nio.channels.FileChannel;
42 import java.nio.channels.GatheringByteChannel;
43 import java.nio.channels.InterruptibleChannel;
44 import java.nio.channels.NetworkChannel;
45 import java.nio.channels.ReadableByteChannel;
46 import java.nio.channels.ScatteringByteChannel;
47 import java.nio.channels.SeekableByteChannel;
48 import java.nio.channels.WritableByteChannel;
49 import java.nio.file.Path;
50 import java.util.List;
51 import java.util.stream.Stream;
52
53 import org.apache.commons.io.FileUtils;
54 import org.apache.commons.lang3.ClassUtils;
55 import org.junit.jupiter.api.Test;
56 import org.junit.jupiter.api.io.TempDir;
57 import org.junit.jupiter.params.ParameterizedTest;
58 import org.junit.jupiter.params.provider.MethodSource;
59
60
61
62
63 class CloseShieldChannelTest {
64
65
66
67
68 static Stream<Class<? extends Channel>> channelInterfaces() {
69
70 return Stream.of(
71 AsynchronousChannel.class,
72 ByteChannel.class,
73 Channel.class,
74 GatheringByteChannel.class,
75 InterruptibleChannel.class,
76 NetworkChannel.class,
77 ReadableByteChannel.class,
78 ScatteringByteChannel.class,
79 SeekableByteChannel.class,
80 WritableByteChannel.class);
81
82 }
83
84
85
86
87 static List<Class<?>> fileChannelInterfaces() {
88 return ClassUtils.getAllInterfaces(FileChannel.class);
89 }
90
91 @ParameterizedTest
92 @MethodSource("channelInterfaces")
93 void testCloseDoesNotCloseDelegate(final Class<? extends Channel> channelClass) throws Exception {
94 final Channel channel = mock(channelClass);
95 final Channel shield = CloseShieldChannel.wrap(channel);
96 shield.close();
97 verify(channel, never()).close();
98 }
99
100 @ParameterizedTest
101 @MethodSource("channelInterfaces")
102 void testCloseIsIdempotent(final Class<? extends Channel> channelClass) throws Exception {
103 final Channel channel = mock(channelClass);
104 final Channel shield = CloseShieldChannel.wrap(channel);
105 shield.close();
106 assertFalse(shield.isOpen());
107 shield.close();
108 assertFalse(shield.isOpen());
109 verifyNoInteractions(channel);
110 }
111
112 @ParameterizedTest
113 @MethodSource("channelInterfaces")
114 void testCloseIsShielded(final Class<? extends Channel> channelInterface) throws Exception {
115 final Channel channel = mock(channelInterface);
116 when(channel.isOpen()).thenReturn(true, false, true, false);
117 final Channel shield = CloseShieldChannel.wrap(channel);
118
119 assertTrue(shield.isOpen(), "isOpen reflects delegate state");
120 assertFalse(shield.isOpen(), "isOpen reflects delegate state");
121 verify(channel, times(2)).isOpen();
122 shield.close();
123
124 assertFalse(shield.isOpen(), "isOpen reflects shield state");
125 assertFalse(shield.isOpen(), "isOpen reflects shield state");
126 verify(channel, times(2)).isOpen();
127 }
128
129 @Test
130 void testDoesNotDoubleWrap() {
131 final ByteChannel channel = mock(ByteChannel.class);
132 final ByteChannel shield1 = CloseShieldChannel.wrap(channel);
133 final ByteChannel shield2 = CloseShieldChannel.wrap(shield1);
134 assertSame(shield1, shield2);
135 }
136
137 @ParameterizedTest
138 @MethodSource("channelInterfaces")
139 void testEquals(final Class<? extends Channel> channelClass) throws Exception {
140 final Channel channel = mock(channelClass);
141 final Channel shield = CloseShieldChannel.wrap(channel);
142 final Channel anotherShield = CloseShieldChannel.wrap(channel);
143 assertTrue(shield.equals(shield), "reflexive");
144 assertFalse(shield.equals(null), "null is not equal");
145 assertFalse(shield.equals(channel), "shield not equal to delegate");
146 assertTrue(shield.equals(anotherShield), "shields of same delegate are equal");
147 }
148
149 @Test
150 void testGatheringByteChannelMethods() throws Exception {
151 final GatheringByteChannel channel = mock(GatheringByteChannel.class);
152 when(channel.isOpen()).thenReturn(true);
153 final GatheringByteChannel shield = CloseShieldChannel.wrap(channel);
154
155 when(channel.write(null, 0, 0)).thenReturn(42L);
156 assertEquals(42, shield.write(null, 0, 0));
157 verify(channel).write(null, 0, 0);
158
159 shield.close();
160 assertThrows(ClosedChannelException.class, () -> shield.write(null, 0, 0));
161 verifyNoMoreInteractions(channel);
162 }
163
164 @ParameterizedTest
165 @MethodSource("channelInterfaces")
166 void testHashCode(final Class<? extends Channel> channelClass) throws Exception {
167 final Channel channel = mock(channelClass);
168 final Channel shield = CloseShieldChannel.wrap(channel);
169 final Channel anotherShield = CloseShieldChannel.wrap(channel);
170 assertEquals(shield.hashCode(), channel.hashCode(), "delegates hashCode");
171 assertEquals(shield.hashCode(), anotherShield.hashCode(), "shields of same delegate have same hashCode");
172 }
173
174 @Test
175 void testNetworkChannelMethods() throws Exception {
176 final NetworkChannel channel = mock(NetworkChannel.class);
177 when(channel.isOpen()).thenReturn(true);
178 final NetworkChannel shield = CloseShieldChannel.wrap(channel);
179
180 when(channel.getOption(null)).thenReturn("foo");
181 when(channel.setOption(null, null)).thenReturn(channel);
182 when(channel.getLocalAddress()).thenReturn(null);
183 when(channel.bind(null)).thenReturn(channel);
184 assertEquals("foo", shield.getOption(null));
185 assertEquals(shield, shield.setOption(null, null));
186 assertEquals(null, shield.getLocalAddress());
187 assertEquals(shield, shield.bind(null));
188 verify(channel).getOption(null);
189 verify(channel).setOption(null, null);
190 verify(channel).getLocalAddress();
191 verify(channel).bind(null);
192
193 shield.close();
194 assertDoesNotThrow(shield::supportedOptions);
195 verify(channel).supportedOptions();
196
197 assertThrows(ClosedChannelException.class, () -> shield.setOption(null, null));
198 assertThrows(ClosedChannelException.class, () -> shield.getOption(null));
199 assertThrows(ClosedChannelException.class, shield::getLocalAddress);
200 assertThrows(ClosedChannelException.class, () -> shield.bind(null));
201 verifyNoMoreInteractions(channel);
202 }
203
204 @ParameterizedTest
205 @MethodSource("channelInterfaces")
206 void testPreservesInterfaces(final Class<? extends Channel> channelClass) {
207 final Channel channel = mock(channelClass);
208 final Channel shield = CloseShieldChannel.wrap(channel);
209 assertNotSame(channel, shield);
210 assertTrue(channelClass.isInstance(shield));
211 }
212
213 @Test
214 void testReadableByteChannelMethods() throws Exception {
215 final ReadableByteChannel channel = mock(ReadableByteChannel.class);
216 when(channel.isOpen()).thenReturn(true);
217 final ReadableByteChannel shield = CloseShieldChannel.wrap(channel);
218
219 when(channel.read(null)).thenReturn(42);
220 assertEquals(42, shield.read(null));
221 verify(channel).read(null);
222
223 shield.close();
224 assertThrows(ClosedChannelException.class, () -> shield.read(null));
225 verifyNoMoreInteractions(channel);
226 }
227
228 @Test
229 void testScatteringByteChannelMethods() throws Exception {
230 final ScatteringByteChannel channel = mock(ScatteringByteChannel.class);
231 when(channel.isOpen()).thenReturn(true);
232 final ScatteringByteChannel shield = CloseShieldChannel.wrap(channel);
233
234 when(channel.read(null, 0, 0)).thenReturn(42L);
235 assertEquals(42, shield.read(null, 0, 0));
236 verify(channel).read(null, 0, 0);
237
238 shield.close();
239 assertThrows(ClosedChannelException.class, () -> shield.read(null, 0, 0));
240 verifyNoMoreInteractions(channel);
241 }
242
243 @Test
244 void testSeekableByteChannelMethods() throws Exception {
245 final SeekableByteChannel channel = mock(SeekableByteChannel.class);
246 when(channel.isOpen()).thenReturn(true);
247 final SeekableByteChannel shield = CloseShieldChannel.wrap(channel);
248
249 when(channel.position()).thenReturn(42L);
250 when(channel.size()).thenReturn(84L);
251 assertEquals(42, shield.position());
252 assertEquals(84, shield.size());
253 verify(channel).position();
254 verify(channel).size();
255
256 when(channel.position(21)).thenReturn(channel);
257 when(channel.truncate(21)).thenReturn(channel);
258 assertEquals(shield, shield.position(21));
259 assertEquals(shield, shield.truncate(21));
260 verify(channel).position(21);
261 verify(channel).truncate(21);
262
263 shield.close();
264 assertThrows(ClosedChannelException.class, shield::position);
265 assertThrows(ClosedChannelException.class, () -> shield.position(0));
266 assertThrows(ClosedChannelException.class, shield::size);
267 assertThrows(ClosedChannelException.class, () -> shield.truncate(0));
268 verifyNoMoreInteractions(channel);
269 }
270
271 @ParameterizedTest
272 @MethodSource("channelInterfaces")
273 void testToString(final Class<? extends Channel> channelClass) throws Exception {
274 final Channel channel = mock(channelClass);
275 when(channel.toString()).thenReturn("MyChannel");
276 final Channel shield = CloseShieldChannel.wrap(channel);
277 final String shieldString = shield.toString();
278 assertTrue(shieldString.contains("CloseShield"));
279 assertTrue(shieldString.contains("MyChannel"));
280 }
281
282 @Test
283 void testWrapFileChannel(final @TempDir Path tempDir) throws IOException {
284 final Path testFile = tempDir.resolve("test.txt");
285 FileUtils.touch(testFile.toFile());
286 try (FileChannel channel = FileChannel.open(testFile); Channel shield = CloseShieldChannel.wrap(channel)) {
287 fileChannelInterfaces().forEach(iface -> assertInstanceOf(iface, shield));
288
289 assertFalse(shield instanceof FileChannel, "not FileChannel");
290 }
291 }
292
293 @Test
294 void testWritableByteChannelMethods() throws Exception {
295 final WritableByteChannel channel = mock(WritableByteChannel.class);
296 when(channel.isOpen()).thenReturn(true);
297 final WritableByteChannel shield = CloseShieldChannel.wrap(channel);
298
299 when(channel.write(null)).thenReturn(42);
300 assertEquals(42, shield.write(null));
301 verify(channel).write(null);
302
303 shield.close();
304 assertThrows(ClosedChannelException.class, () -> shield.write(null));
305 verifyNoMoreInteractions(channel);
306 }
307 }