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 protected 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
91 @Test
92 void testConcurrentPositionAndSizeQueries() throws IOException {
93 final byte[] data = "test data".getBytes();
94 channel.write(ByteBuffer.wrap(data));
95 final long size = channel.size();
96 final long position = channel.position();
97 assertEquals(data.length, size);
98 assertEquals(data.length, position);
99
100 assertEquals(channel.size(), size);
101 assertEquals(channel.position(), position);
102 }
103
104 @Test
105 void testIsOpenAfterClose() throws IOException {
106 channel.close();
107 assertFalse(channel.isOpen());
108 }
109
110 @Test
111 void testIsOpennOnNew() {
112 assertTrue(channel.isOpen());
113 }
114
115 @Test
116 void testPartialWritesAndReads() throws IOException {
117 final byte[] data = "0123456789".getBytes();
118
119 channel.write(ByteBuffer.wrap(data, 0, 5));
120 channel.write(ByteBuffer.wrap(data, 5, 5));
121 assertEquals(10, channel.size());
122
123 channel.position(0);
124 final ByteBuffer buffer1 = ByteBuffer.allocate(3);
125 final ByteBuffer buffer2 = ByteBuffer.allocate(7);
126 assertEquals(3, channel.read(buffer1));
127 assertEquals(7, channel.read(buffer2));
128 assertArrayEquals(Arrays.copyOf(data, 3), buffer1.array());
129 assertArrayEquals(Arrays.copyOfRange(data, 3, 10), buffer2.array());
130 }
131
132 @Test
133 void testPositionBeyondSize() throws IOException {
134 channel.write(ByteBuffer.wrap("test".getBytes()));
135 channel.position(100);
136 assertEquals(100, channel.position());
137 assertEquals(4, channel.size());
138 }
139
140 @Test
141 void testPositionBeyondSizeRead() throws IOException {
142 final ByteBuffer buffer = ByteBuffer.allocate(1);
143 channel.position(channel.size() + 1);
144 assertEquals(channel.size() + 1, channel.position());
145 assertEquals(-1, channel.read(buffer));
146 channel.position(Integer.MAX_VALUE + 1L);
147 assertEquals(Integer.MAX_VALUE + 1L, channel.position());
148 assertEquals(-1, channel.read(buffer));
149 assertThrows(IllegalArgumentException.class, () -> channel.position(-1));
150 assertThrows(IllegalArgumentException.class, () -> channel.position(Integer.MIN_VALUE));
151 assertThrows(IllegalArgumentException.class, () -> channel.position(Long.MIN_VALUE));
152 }
153
154 @ParameterizedTest
155 @CsvSource({ "0, 0", "5, 5", "10, 10", "100, 100" })
156 void testPositionInBounds(final long newPosition, final long expectedPosition) throws IOException {
157
158 final byte[] data = new byte[200];
159 random.nextBytes(data);
160 channel.write(ByteBuffer.wrap(data));
161 @SuppressWarnings("resource")
162 final SeekableByteChannel result = channel.position(newPosition);
163 assertSame(channel, result);
164 assertEquals(expectedPosition, channel.position());
165 }
166
167
168 @Test
169 void testPositionNegative() {
170 assertThrows(IllegalArgumentException.class, () -> channel.position(-1));
171 }
172
173 @Test
174 void testPositionOnClosed() throws IOException {
175 channel.close();
176 assertThrows(ClosedChannelException.class, () -> channel.position());
177 assertThrows(ClosedChannelException.class, () -> channel.position(0));
178 }
179
180 @Test
181 void testPositionOnNew() throws IOException {
182 assertEquals(0, channel.position());
183 }
184
185 @Test
186 void testRandomAccess() throws IOException {
187 final byte[] data = new byte[1000];
188 random.nextBytes(data);
189
190 channel.write(ByteBuffer.wrap(data));
191
192 final int[] positions = { 100, 500, 0, 999, 250 };
193 for (final int pos : positions) {
194 channel.position(pos);
195 assertEquals(pos, channel.position());
196 final ByteBuffer buffer = ByteBuffer.allocate(1);
197 final int read = channel.read(buffer);
198 if (pos < data.length) {
199 assertEquals(1, read);
200 assertEquals(data[pos], buffer.get(0));
201 }
202 }
203 }
204
205 @Test
206 void testReadAtEndOfFile() throws IOException {
207 channel.write(ByteBuffer.wrap("test".getBytes()));
208
209 final ByteBuffer buffer = ByteBuffer.allocate(10);
210 final int read = channel.read(buffer);
211 assertEquals(-1, read);
212 }
213
214 @Test
215 void testReadBuffer() throws IOException {
216 final byte[] data = "test".getBytes();
217 channel.write(ByteBuffer.wrap(data));
218 channel.position(0);
219 final ByteBuffer buffer = ByteBuffer.allocate(100);
220 final int read = channel.read(buffer);
221 assertEquals(data.length, read);
222 assertEquals(data.length, channel.position());
223
224 final byte[] readData = new byte[read];
225 buffer.flip();
226 buffer.get(readData);
227 assertArrayEquals(data, readData);
228 }
229
230 @Test
231 void testReadBytes() throws IOException {
232 final byte[] data = "Hello, World!".getBytes();
233 channel.write(ByteBuffer.wrap(data));
234 channel.position(0);
235 final ByteBuffer buffer = ByteBuffer.allocate(data.length);
236 final int read = channel.read(buffer);
237 assertEquals(data.length, read);
238 assertArrayEquals(data, buffer.array());
239 assertEquals(data.length, channel.position());
240 }
241
242 @Test
243 void testReadClosed() throws IOException {
244 channel.close();
245 final ByteBuffer buffer = ByteBuffer.allocate(10);
246 assertThrows(ClosedChannelException.class, () -> channel.read(buffer));
247 }
248
249 @Test
250 void testReadEmpty() throws IOException {
251 final ByteBuffer buffer = ByteBuffer.allocate(10);
252 final int read = channel.read(buffer);
253 assertEquals(-1, read);
254 assertEquals(0, buffer.position());
255 }
256
257 @Test
258 void testReadNull() {
259 assertThrows(NullPointerException.class, () -> channel.read(null));
260 }
261
262 @Test
263 void testReadSingleByte() throws IOException {
264
265 channel.write(ByteBuffer.wrap(new byte[] { 42 }));
266 channel.position(0);
267 final ByteBuffer buffer = ByteBuffer.allocate(1);
268 final int read = channel.read(buffer);
269 assertEquals(1, read);
270 assertEquals(42, buffer.get(0));
271 assertEquals(1, channel.position());
272 }
273
274 @Test
275 void testSizeAfterTruncateToLargerSize() throws IOException {
276 channel.write(ByteBuffer.wrap("Hello".getBytes()));
277 assertEquals(5, channel.size());
278 channel.truncate(10);
279 assertEquals(5, channel.size());
280 }
281
282 @Test
283 void testSizeAfterWrite() throws IOException {
284 assertEquals(0, channel.size());
285 channel.write(ByteBuffer.wrap("Hello".getBytes()));
286 assertEquals(5, channel.size());
287 channel.write(ByteBuffer.wrap(" World".getBytes()));
288 assertEquals(11, channel.size());
289 }
290
291 @Test
292 void testSizeOnClosed() throws IOException {
293 channel.close();
294 assertThrows(ClosedChannelException.class, () -> channel.size());
295 }
296
297 @Test
298 void testSizeOnNew() throws IOException {
299 assertEquals(0, channel.size());
300 }
301
302 @Test
303 void testSizeSameOnOverwrite() throws IOException {
304 channel.write(ByteBuffer.wrap("Hello World".getBytes()));
305 assertEquals(11, channel.size());
306 channel.position(6);
307 channel.write(ByteBuffer.wrap("Test".getBytes()));
308 assertEquals(11, channel.size());
309 }
310
311 @Test
312 void testTrucateBeyondSizeReadWrite() throws IOException {
313 final ByteBuffer buffer = ByteBuffer.allocate(1);
314 channel.truncate(channel.size() + 1);
315 assertEquals(-1, channel.read(buffer));
316 channel.truncate(Integer.MAX_VALUE + 1L);
317 assertEquals(-1, channel.read(buffer));
318 assertThrows(IllegalArgumentException.class, () -> channel.truncate(-1));
319 assertThrows(IllegalArgumentException.class, () -> channel.truncate(Integer.MIN_VALUE));
320 assertThrows(IllegalArgumentException.class, () -> channel.truncate(Long.MIN_VALUE));
321 }
322
323 @Test
324 void testTruncateNegative() {
325 assertThrows(IllegalArgumentException.class, () -> channel.truncate(-1));
326 }
327
328 @Test
329 void testTruncateShrinks() throws IOException {
330 channel.write(ByteBuffer.wrap("Hello World".getBytes()));
331 assertEquals(11, channel.size());
332 channel.truncate(5);
333 assertEquals(5, channel.size());
334
335 if (channel.position() > 5) {
336 assertEquals(5, channel.position());
337 }
338 }
339
340 @Test
341 void testWriteBeyondSizeGrows() throws IOException {
342 channel.position(100);
343 final byte[] data = "test".getBytes();
344 channel.write(ByteBuffer.wrap(data));
345 assertEquals(104, channel.size());
346 assertEquals(104, channel.position());
347
348 channel.position(0);
349 final ByteBuffer buffer = ByteBuffer.allocate(100);
350 channel.read(buffer);
351
352 final byte[] expectedGap = new byte[100];
353 assertArrayEquals(expectedGap, buffer.array());
354 }
355
356 @ParameterizedTest
357 @ValueSource(ints = { 1, 10, 100, 1000, 10000 })
358 void testWriteDifferentSizes(final int size) throws IOException {
359 final byte[] data = new byte[size];
360 random.nextBytes(data);
361 final ByteBuffer buffer = ByteBuffer.wrap(data);
362 final int byteCount = channel.write(buffer);
363 assertEquals(size, byteCount);
364 assertEquals(size, channel.position());
365 assertEquals(size, channel.size());
366 }
367 @Test
368 void testWriteEmpty() throws IOException {
369 final ByteBuffer buffer = ByteBuffer.allocate(0);
370 final int byteCount = channel.write(buffer);
371 assertEquals(0, byteCount);
372 assertEquals(0, channel.position());
373 assertEquals(0, channel.size());
374 }
375 @Test
376 void testWriteNull() {
377 assertThrows(NullPointerException.class, () -> channel.write(null));
378 }
379 @Test
380 void testWritePositionReadVerify() throws IOException {
381 final byte[] originalData = "Hello, SeekableByteChannel World!".getBytes();
382
383 channel.write(ByteBuffer.wrap(originalData));
384
385 channel.position(0);
386
387 final ByteBuffer readBuffer = ByteBuffer.allocate(originalData.length);
388 final int bytesRead = channel.read(readBuffer);
389 assertEquals(originalData.length, bytesRead);
390 assertArrayEquals(originalData, readBuffer.array());
391 }
392
393 @Test
394 void testWriteSingleByte() throws IOException {
395 final ByteBuffer buffer = ByteBuffer.allocate(1);
396 buffer.put((byte) 42);
397 buffer.flip();
398 final int written = channel.write(buffer);
399 assertEquals(1, written);
400 assertEquals(1, channel.position());
401 assertEquals(1, channel.size());
402 }
403
404 @Test
405 void testWriteToClosedChannel() throws IOException {
406 channel.close();
407 final ByteBuffer buffer = ByteBuffer.wrap("test".getBytes());
408 assertThrows(ClosedChannelException.class, () -> channel.write(buffer));
409 }
410
411 @Test
412 void tesWriteBytes() throws IOException {
413 final byte[] data = "Hello, World!".getBytes();
414 final ByteBuffer buffer = ByteBuffer.wrap(data);
415 final int byteCount = channel.write(buffer);
416 assertEquals(data.length, byteCount);
417 assertEquals(data.length, channel.position());
418 assertEquals(data.length, channel.size());
419 }
420 }