Skip to content

Commit 93de0e4

Browse files
committed
Add CloseShieldChannelFilterByteChannelTest
1 parent 2ab33b4 commit 93de0e4

1 file changed

Lines changed: 168 additions & 0 deletions

File tree

Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* https://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
package org.apache.commons.io.channels;
19+
20+
import static org.junit.jupiter.api.Assertions.assertEquals;
21+
import static org.junit.jupiter.api.Assertions.assertFalse;
22+
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
23+
import static org.junit.jupiter.api.Assertions.assertSame;
24+
import static org.junit.jupiter.api.Assertions.assertThrows;
25+
import static org.junit.jupiter.api.Assertions.assertTrue;
26+
import static org.mockito.Mockito.mock;
27+
import static org.mockito.Mockito.never;
28+
import static org.mockito.Mockito.times;
29+
import static org.mockito.Mockito.verify;
30+
import static org.mockito.Mockito.when;
31+
32+
import java.io.IOException;
33+
import java.nio.ByteBuffer;
34+
import java.nio.channels.ByteChannel;
35+
import java.nio.channels.ClosedChannelException;
36+
37+
import org.junit.jupiter.api.BeforeEach;
38+
import org.junit.jupiter.api.Test;
39+
import org.mockito.ArgumentMatchers;
40+
41+
/**
42+
* Tests {@link CloseShieldChannel} wrapping a {@link FilterByteChannel}.
43+
*/
44+
class CloseShieldChannelFilterByteChannelTest {
45+
46+
/** A FilterByteChannel wrapping the mock. */
47+
private FilterByteChannel<ByteChannel> filterChannel;
48+
49+
/** The innermost mock delegate. */
50+
private ByteChannel mockChannel;
51+
52+
/** The CloseShieldChannel wrapping the FilterByteChannel. */
53+
private ByteChannel shield;
54+
55+
@BeforeEach
56+
void setUp() throws IOException {
57+
mockChannel = mock(ByteChannel.class);
58+
filterChannel = FilterByteChannel.forByteChannel().setChannel(mockChannel).get();
59+
shield = CloseShieldChannel.wrap(filterChannel);
60+
}
61+
62+
@Test
63+
void testCloseDoesNotCloseFilterByteChannel() throws IOException {
64+
when(mockChannel.isOpen()).thenReturn(true);
65+
shield.close();
66+
// The FilterByteChannel (and thus the mock) must not have been closed.
67+
verify(mockChannel, never()).close();
68+
assertTrue(filterChannel.isOpen(), "FilterByteChannel must still be open");
69+
}
70+
71+
@Test
72+
void testCloseIsIdempotent() throws IOException {
73+
shield.close();
74+
shield.close();
75+
assertFalse(shield.isOpen());
76+
verify(mockChannel, never()).close();
77+
}
78+
79+
@Test
80+
void testDoubleWrapReturnsSameShield() {
81+
final ByteChannel shield2 = CloseShieldChannel.wrap(shield);
82+
assertSame(shield, shield2, "wrapping an existing shield must return the same proxy");
83+
}
84+
85+
@Test
86+
void testFilterByteChannelUsableAfterShieldClose() throws IOException {
87+
when(mockChannel.isOpen()).thenReturn(true);
88+
shield.close();
89+
// The shield is closed, but the underlying FilterByteChannel must still work.
90+
assertTrue(filterChannel.isOpen());
91+
final ByteBuffer buf = ByteBuffer.allocate(4);
92+
when(mockChannel.read(buf)).thenReturn(4);
93+
assertEquals(4, filterChannel.read(buf));
94+
verify(mockChannel).read(buf);
95+
}
96+
97+
@Test
98+
void testIsOpenDelegatesBeforeShieldClose() throws IOException {
99+
when(mockChannel.isOpen()).thenReturn(true, false);
100+
assertTrue(shield.isOpen(), "reflects open delegate");
101+
assertFalse(shield.isOpen(), "reflects closed delegate");
102+
verify(mockChannel, times(2)).isOpen();
103+
}
104+
105+
@Test
106+
void testIsOpenReturnsFalseAfterShieldClose() throws IOException {
107+
when(mockChannel.isOpen()).thenReturn(true);
108+
shield.close();
109+
assertFalse(shield.isOpen(), "shield is closed so isOpen must return false");
110+
// isOpen must NOT be forwarded to the delegate after the shield is closed.
111+
verify(mockChannel, never()).isOpen();
112+
}
113+
114+
@Test
115+
void testReadDelegatesToFilterByteChannel() throws IOException {
116+
final ByteBuffer buf = ByteBuffer.allocate(16);
117+
when(mockChannel.read(buf)).thenReturn(8);
118+
when(mockChannel.isOpen()).thenReturn(true);
119+
assertEquals(8, shield.read(buf));
120+
verify(mockChannel).read(buf);
121+
}
122+
123+
@Test
124+
void testReadIOExceptionPropagates() throws IOException {
125+
final ByteBuffer buf = ByteBuffer.allocate(8);
126+
when(mockChannel.isOpen()).thenReturn(true);
127+
when(mockChannel.read(buf)).thenThrow(new IOException("read failure"));
128+
assertThrows(IOException.class, () -> shield.read(buf));
129+
verify(mockChannel).read(buf);
130+
}
131+
132+
@Test
133+
void testReadThrowsClosedChannelExceptionAfterShieldClose() throws IOException {
134+
shield.close();
135+
assertThrows(ClosedChannelException.class, () -> shield.read(ByteBuffer.allocate(8)));
136+
verify(mockChannel, never()).read(ArgumentMatchers.any());
137+
}
138+
139+
@Test
140+
void testShieldImplementsByteChannel() {
141+
assertInstanceOf(ByteChannel.class, shield);
142+
}
143+
144+
@Test
145+
void testWriteDelegatesToFilterByteChannel() throws IOException {
146+
final ByteBuffer buf = ByteBuffer.allocate(16);
147+
when(mockChannel.write(buf)).thenReturn(16);
148+
when(mockChannel.isOpen()).thenReturn(true);
149+
assertEquals(16, shield.write(buf));
150+
verify(mockChannel).write(buf);
151+
}
152+
153+
@Test
154+
void testWriteIOExceptionPropagates() throws IOException {
155+
final ByteBuffer buf = ByteBuffer.allocate(8);
156+
when(mockChannel.isOpen()).thenReturn(true);
157+
when(mockChannel.write(buf)).thenThrow(new IOException("write failure"));
158+
assertThrows(IOException.class, () -> shield.write(buf));
159+
verify(mockChannel).write(buf);
160+
}
161+
162+
@Test
163+
void testWriteThrowsClosedChannelExceptionAfterShieldClose() throws IOException {
164+
shield.close();
165+
assertThrows(ClosedChannelException.class, () -> shield.write(ByteBuffer.allocate(8)));
166+
verify(mockChannel, never()).write(ArgumentMatchers.any());
167+
}
168+
}

0 commit comments

Comments
 (0)