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.assertArrayEquals;
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.assertSame;
24 import static org.junit.jupiter.api.Assertions.assertThrows;
25 import static org.junit.jupiter.api.Assertions.assertTrue;
26
27 import java.io.IOException;
28 import java.nio.ByteBuffer;
29 import java.nio.channels.ClosedChannelException;
30 import java.nio.channels.SeekableByteChannel;
31 import java.nio.file.Files;
32 import java.nio.file.Path;
33 import java.util.Arrays;
34 import java.util.Random;
35
36 import org.junit.jupiter.api.AfterEach;
37 import org.junit.jupiter.api.BeforeEach;
38 import org.junit.jupiter.api.Test;
39 import org.junit.jupiter.api.io.TempDir;
40 import org.junit.jupiter.params.ParameterizedTest;
41 import org.junit.jupiter.params.provider.CsvSource;
42 import org.junit.jupiter.params.provider.ValueSource;
43
44
45
46
47
48 abstract class AbstractSeekableByteChannelTest {
49
50 private SeekableByteChannel channel;
51
52 @TempDir
53 protected Path tempDir;
54
55 protected Path tempFile;
56
57 private final Random random = new Random(42);
58
59
60
61
62
63
64
65 protected abstract SeekableByteChannel createChannel() throws IOException;
66
67 @BeforeEach
68 void setUp() throws IOException {
69 tempFile = tempDir.resolve(getClass().getSimpleName() + ".tmp");
70 channel = createChannel();
71 }
72
73 @AfterEach
74 void tearDown() throws IOException {
75 if (channel != null && channel.isOpen()) {
76 channel.close();
77 }
78 if (tempFile != null && Files.exists(tempFile)) {
79 Files.delete(tempFile);
80 }
81 }
82
83 @Test
84 void testCloseMultipleTimes() throws IOException {
85 channel.close();
86 channel.close();
87 assertFalse(channel.isOpen());
88 }
89
90 @Test
91 void testConcurrentPositionAndSizeQueries() throws IOException {
92 final byte[] data = "test data".getBytes();
93 channel.write(ByteBuffer.wrap(data));
94 final long size = channel.size();
95 final long position = channel.position();
96 assertEquals(data.length, size);
97 assertEquals(data.length, position);
98
99 assertEquals(channel.size(), size);
100 assertEquals(channel.position(), position);
101 }
102
103 @Test
104 void testIsOpenAfterClose() throws IOException {
105 channel.close();
106 assertFalse(channel.isOpen());
107 }
108
109 @Test
110 void testIsOpennOnNew() {
111 assertTrue(channel.isOpen());
112 }
113
114 @Test
115 void testPartialWritesAndReads() throws IOException {
116 final byte[] data = "0123456789".getBytes();
117
118 channel.write(ByteBuffer.wrap(data, 0, 5));
119 channel.write(ByteBuffer.wrap(data, 5, 5));
120 assertEquals(10, channel.size());
121
122 channel.position(0);
123 final ByteBuffer buffer1 = ByteBuffer.allocate(3);
124 final ByteBuffer buffer2 = ByteBuffer.allocate(7);
125 assertEquals(3, channel.read(buffer1));
126 assertEquals(7, channel.read(buffer2));
127 assertArrayEquals(Arrays.copyOf(data, 3), buffer1.array());
128 assertArrayEquals(Arrays.copyOfRange(data, 3, 10), buffer2.array());
129 }
130
131 @Test
132 void testPositionBeyondSize() throws IOException {
133 channel.write(ByteBuffer.wrap("test".getBytes()));
134 channel.position(100);
135 assertEquals(100, channel.position());
136 assertEquals(4, channel.size());
137 }
138
139 @ParameterizedTest
140 @CsvSource({ "0, 0", "5, 5", "10, 10", "100, 100" })
141 void testPositionInBounds(final long newPosition, final long expectedPosition) throws IOException {
142
143 final byte[] data = new byte[200];
144 random.nextBytes(data);
145 channel.write(ByteBuffer.wrap(data));
146 @SuppressWarnings("resource")
147 final SeekableByteChannel result = channel.position(newPosition);
148 assertSame(channel, result);
149 assertEquals(expectedPosition, channel.position());
150 }
151
152 @Test
153 void testPositionNegative() {
154 assertThrows(IllegalArgumentException.class, () -> channel.position(-1));
155 }
156
157 @Test
158 void testPositionOnClosed() throws IOException {
159 channel.close();
160 assertThrows(ClosedChannelException.class, () -> channel.position());
161 assertThrows(ClosedChannelException.class, () -> channel.position(0));
162 }
163
164 @Test
165 void testPositionOnNew() throws IOException {
166 assertEquals(0, channel.position());
167 }
168
169 @Test
170 void testRandomAccess() throws IOException {
171 final byte[] data = new byte[1000];
172 random.nextBytes(data);
173
174 channel.write(ByteBuffer.wrap(data));
175
176 final int[] positions = { 100, 500, 0, 999, 250 };
177 for (final int pos : positions) {
178 channel.position(pos);
179 assertEquals(pos, channel.position());
180 final ByteBuffer buffer = ByteBuffer.allocate(1);
181 final int read = channel.read(buffer);
182 if (pos < data.length) {
183 assertEquals(1, read);
184 assertEquals(data[pos], buffer.get(0));
185 }
186 }
187 }
188
189 @Test
190 void testReadAtEndOfFile() throws IOException {
191 channel.write(ByteBuffer.wrap("test".getBytes()));
192
193 final ByteBuffer buffer = ByteBuffer.allocate(10);
194 final int read = channel.read(buffer);
195 assertEquals(-1, read);
196 }
197
198 @Test
199 void testReadBuffer() throws IOException {
200 final byte[] data = "test".getBytes();
201 channel.write(ByteBuffer.wrap(data));
202 channel.position(0);
203 final ByteBuffer buffer = ByteBuffer.allocate(100);
204 final int read = channel.read(buffer);
205 assertEquals(data.length, read);
206 assertEquals(data.length, channel.position());
207
208 final byte[] readData = new byte[read];
209 buffer.flip();
210 buffer.get(readData);
211 assertArrayEquals(data, readData);
212 }
213
214 @Test
215 void testReadBytes() throws IOException {
216 final byte[] data = "Hello, World!".getBytes();
217 channel.write(ByteBuffer.wrap(data));
218 channel.position(0);
219 final ByteBuffer buffer = ByteBuffer.allocate(data.length);
220 final int read = channel.read(buffer);
221 assertEquals(data.length, read);
222 assertArrayEquals(data, buffer.array());
223 assertEquals(data.length, channel.position());
224 }
225
226 @Test
227 void testReadClosed() throws IOException {
228 channel.close();
229 final ByteBuffer buffer = ByteBuffer.allocate(10);
230 assertThrows(ClosedChannelException.class, () -> channel.read(buffer));
231 }
232
233 @Test
234 void testReadEmpty() throws IOException {
235 final ByteBuffer buffer = ByteBuffer.allocate(10);
236 final int read = channel.read(buffer);
237 assertEquals(-1, read);
238 assertEquals(0, buffer.position());
239 }
240
241 @Test
242 void testReadNull() {
243 assertThrows(NullPointerException.class, () -> channel.read(null));
244 }
245
246 @Test
247 void testReadSingleByte() throws IOException {
248
249 channel.write(ByteBuffer.wrap(new byte[] { 42 }));
250 channel.position(0);
251 final ByteBuffer buffer = ByteBuffer.allocate(1);
252 final int read = channel.read(buffer);
253 assertEquals(1, read);
254 assertEquals(42, buffer.get(0));
255 assertEquals(1, channel.position());
256 }
257
258 @Test
259 void testSizeAfterTruncateToLargerSize() throws IOException {
260 channel.write(ByteBuffer.wrap("Hello".getBytes()));
261 assertEquals(5, channel.size());
262 channel.truncate(10);
263 assertEquals(5, channel.size());
264 }
265
266 @Test
267 void testSizeAfterWrite() throws IOException {
268 assertEquals(0, channel.size());
269 channel.write(ByteBuffer.wrap("Hello".getBytes()));
270 assertEquals(5, channel.size());
271 channel.write(ByteBuffer.wrap(" World".getBytes()));
272 assertEquals(11, channel.size());
273 }
274
275 @Test
276 void testSizeOnClosed() throws IOException {
277 channel.close();
278 assertThrows(ClosedChannelException.class, () -> channel.size());
279 }
280
281 @Test
282 void testSizeOnNew() throws IOException {
283 assertEquals(0, channel.size());
284 }
285
286 @Test
287 void testSizeSameOnOverwrite() throws IOException {
288 channel.write(ByteBuffer.wrap("Hello World".getBytes()));
289 assertEquals(11, channel.size());
290 channel.position(6);
291 channel.write(ByteBuffer.wrap("Test".getBytes()));
292 assertEquals(11, channel.size());
293 }
294
295 @Test
296 void testTruncateNegative() {
297 assertThrows(IllegalArgumentException.class, () -> channel.truncate(-1));
298 }
299
300 @Test
301 void testTruncateShrinks() throws IOException {
302 channel.write(ByteBuffer.wrap("Hello World".getBytes()));
303 assertEquals(11, channel.size());
304 channel.truncate(5);
305 assertEquals(5, channel.size());
306
307 if (channel.position() > 5) {
308 assertEquals(5, channel.position());
309 }
310 }
311
312 @Test
313 void testWriteBeyondSizeGrows() throws IOException {
314 channel.position(100);
315 final byte[] data = "test".getBytes();
316 channel.write(ByteBuffer.wrap(data));
317 assertEquals(104, channel.size());
318 assertEquals(104, channel.position());
319
320 channel.position(0);
321 final ByteBuffer buffer = ByteBuffer.allocate(100);
322 channel.read(buffer);
323
324 final byte[] expectedGap = new byte[100];
325 assertArrayEquals(expectedGap, buffer.array());
326 }
327
328 @ParameterizedTest
329 @ValueSource(ints = { 1, 10, 100, 1000, 10000 })
330 void testWriteDifferentSizes(final int size) throws IOException {
331 final byte[] data = new byte[size];
332 random.nextBytes(data);
333 final ByteBuffer buffer = ByteBuffer.wrap(data);
334 final int byteCount = channel.write(buffer);
335 assertEquals(size, byteCount);
336 assertEquals(size, channel.position());
337 assertEquals(size, channel.size());
338 }
339 @Test
340 void testWriteEmpty() throws IOException {
341 final ByteBuffer buffer = ByteBuffer.allocate(0);
342 final int byteCount = channel.write(buffer);
343 assertEquals(0, byteCount);
344 assertEquals(0, channel.position());
345 assertEquals(0, channel.size());
346 }
347 @Test
348 void testWriteNull() {
349 assertThrows(NullPointerException.class, () -> channel.write(null));
350 }
351 @Test
352 void testWritePositionReadVerify() throws IOException {
353 final byte[] originalData = "Hello, SeekableByteChannel World!".getBytes();
354
355 channel.write(ByteBuffer.wrap(originalData));
356
357 channel.position(0);
358
359 final ByteBuffer readBuffer = ByteBuffer.allocate(originalData.length);
360 final int bytesRead = channel.read(readBuffer);
361 assertEquals(originalData.length, bytesRead);
362 assertArrayEquals(originalData, readBuffer.array());
363 }
364
365 @Test
366 void testWriteSingleByte() throws IOException {
367 final ByteBuffer buffer = ByteBuffer.allocate(1);
368 buffer.put((byte) 42);
369 buffer.flip();
370 final int written = channel.write(buffer);
371 assertEquals(1, written);
372 assertEquals(1, channel.position());
373 assertEquals(1, channel.size());
374 }
375
376 @Test
377 void testWriteToClosedChannel() throws IOException {
378 channel.close();
379 final ByteBuffer buffer = ByteBuffer.wrap("test".getBytes());
380 assertThrows(ClosedChannelException.class, () -> channel.write(buffer));
381 }
382
383 @Test
384 void tesWriteBytes() throws IOException {
385 final byte[] data = "Hello, World!".getBytes();
386 final ByteBuffer buffer = ByteBuffer.wrap(data);
387 final int byteCount = channel.write(buffer);
388 assertEquals(data.length, byteCount);
389 assertEquals(data.length, channel.position());
390 assertEquals(data.length, channel.size());
391 }
392 }