1 package emissary.util;
2
3 import emissary.test.core.junit5.UnitTest;
4
5 import org.junit.jupiter.api.Test;
6
7 import java.io.ByteArrayInputStream;
8 import java.io.EOFException;
9 import java.nio.ByteBuffer;
10 import java.nio.channels.Channels;
11 import java.nio.channels.ClosedChannelException;
12 import java.nio.channels.ReadableByteChannel;
13
14 import static org.junit.jupiter.api.Assertions.assertArrayEquals;
15 import static org.junit.jupiter.api.Assertions.assertEquals;
16 import static org.junit.jupiter.api.Assertions.assertFalse;
17 import static org.junit.jupiter.api.Assertions.assertThrows;
18 import static org.junit.jupiter.api.Assertions.assertTrue;
19
20
21
22
23 class WindowedSeekableByteChannelTest extends UnitTest {
24
25
26 static final byte LCA = 97;
27 static final byte NULL = 0x00;
28 static final int I_ALPHABET = 26;
29
30 public WindowedSeekableByteChannelTest() {}
31
32
33 private static byte[] buildArry(final int size) {
34 final byte[] b = new byte[size];
35 for (int i = 0; i < b.length;) {
36 final int remaining = b.length - i;
37 final int letterCount = remaining > 25 ? I_ALPHABET : remaining;
38 for (int j = 0; j < letterCount; j++) {
39 b[i++] = (byte) (LCA + j);
40 }
41 }
42 return b;
43 }
44
45 private static ReadableByteChannel getChannel(final int size) {
46 return Channels.newChannel(new ByteArrayInputStream(buildArry(size)));
47 }
48
49 @Test
50 void testConstruction() throws Exception {
51 assertThrows(IllegalArgumentException.class, () -> new WindowedSeekableByteChannel(null, 1));
52 @SuppressWarnings({"resource"})
53 final WindowedSeekableByteChannel instance = new WindowedSeekableByteChannel(getChannel(10), 30);
54 assertEquals(10L, instance.size());
55 assertEquals(10L, instance.getMaxPosition());
56 }
57
58
59
60
61 @Test
62 void testClose() throws Exception {
63 final WindowedSeekableByteChannel instance = new WindowedSeekableByteChannel(getChannel(1), 1);
64 instance.close();
65 assertFalse(instance.isOpen(), "Instance should be closed");
66 boolean ex = false;
67 try {
68 final ByteBuffer dst = ByteBuffer.allocate(1);
69 instance.read(dst);
70 } catch (ClosedChannelException c) {
71 ex = true;
72 }
73 assertTrue(ex, "ClosedChannelException after close");
74 }
75
76
77
78
79 @Test
80 void testRead() throws Exception {
81 final byte[] buff = new byte[I_ALPHABET];
82 final byte[] alphabet = buildArry(I_ALPHABET);
83 final ByteBuffer destination = ByteBuffer.wrap(buff);
84 @SuppressWarnings({"resource"})
85 final WindowedSeekableByteChannel instance = new WindowedSeekableByteChannel(getChannel(I_ALPHABET * 2), 10);
86
87 instance.read(destination);
88 assertArrayEquals(alphabet, destination.array());
89 destination.put(0, NULL);
90 destination.position(0);
91
92 instance.read(destination);
93 assertArrayEquals(alphabet, destination.array());
94 destination.put(0, NULL);
95 destination.position(0);
96
97 final int result = instance.read(destination);
98 assertEquals(-1, result);
99 }
100
101
102
103
104 @Test
105 void testReadOdd() throws Exception {
106 final byte[] buff = new byte[7];
107 final int ODD = 53;
108 final ByteBuffer destination = ByteBuffer.wrap(buff);
109 @SuppressWarnings("resource")
110 final WindowedSeekableByteChannel instance = new WindowedSeekableByteChannel(getChannel(ODD), 19);
111 int result = 0;
112 int read = instance.read(destination);
113 while (read != -1) {
114 result += read;
115 destination.position(0);
116 read = instance.read(destination);
117 }
118
119 assertEquals(ODD, result, "Read " + ODD + " bytes");
120 assertEquals(LCA, buff[(ODD % buff.length) - 1], "Last byte was 'a'");
121 }
122
123
124
125
126 @Test
127 void testReadOddEven() throws Exception {
128
129 final byte[] buff = new byte[17];
130 final ByteBuffer destination = ByteBuffer.wrap(buff);
131 final int EVEN = 1024;
132 final int fourKb = EVEN * 4;
133
134 final int FINAL_ALPHABET_LEN = fourKb % I_ALPHABET;
135 final int LAST_READ_LENGTH = fourKb % buff.length;
136
137 final WindowedSeekableByteChannel instance = new WindowedSeekableByteChannel(getChannel(fourKb), EVEN);
138 int result = 0;
139
140 int read = instance.read(destination);
141 while (read != -1) {
142 result += read;
143 destination.position(0);
144 read = instance.read(destination);
145 }
146 assertEquals(fourKb, result, "Expected to read 4KB");
147 assertEquals(buildArry(I_ALPHABET)[FINAL_ALPHABET_LEN - 1], buff[LAST_READ_LENGTH - 1], "Last byte matches");
148 }
149
150
151
152
153 @Test
154 void testReadEvenOdd() throws Exception {
155 final byte[] buff = new byte[20];
156 final ByteBuffer destination = ByteBuffer.wrap(buff);
157 final int ODD = 47;
158 final int oddTimes3 = ODD * 3;
159
160 final int FINAL_ALPHABET_LEN = oddTimes3 % I_ALPHABET;
161 final int LAST_READ_LENGTH = oddTimes3 % buff.length;
162
163 @SuppressWarnings("resource")
164 final WindowedSeekableByteChannel instance = new WindowedSeekableByteChannel(getChannel(oddTimes3), ODD);
165 int result = 0;
166
167 while (true) {
168 final int read = instance.read(destination);
169 if (read == -1) {
170 break;
171 }
172 result += read;
173 destination.position(0);
174 }
175 assertEquals(oddTimes3, result, "Expected to read " + oddTimes3 + " bytes");
176 assertEquals(buildArry(I_ALPHABET)[FINAL_ALPHABET_LEN - 1], buff[LAST_READ_LENGTH - 1], "Last byte matches");
177 }
178
179
180
181
182 @Test
183 void testPosition_long() throws Exception {
184
185 @SuppressWarnings("resource")
186 WindowedSeekableByteChannel instance = new WindowedSeekableByteChannel(getChannel(26), 20);
187 final ByteBuffer b = ByteBuffer.allocate(1);
188 instance.position(10);
189 instance.read(b);
190 assertEquals(LCA + 10, b.get(0));
191
192 instance.position(0);
193 b.flip();
194 instance.read(b);
195 assertEquals(LCA, b.get(0));
196
197 instance = new WindowedSeekableByteChannel(getChannel(40), 10);
198
199 instance.position(26);
200 b.flip();
201 instance.read(b);
202
203 assertEquals(LCA, b.get(0));
204
205 assertTrue(instance.getMinPosition() > 0);
206 boolean threw = false;
207 try {
208 instance.position(0L);
209 } catch (IllegalStateException ex) {
210 threw = true;
211 }
212 assertTrue(threw, "Did not throw while attempting to move before available window");
213 }
214
215 @Test
216 void testSizePosition() throws Exception {
217 @SuppressWarnings("resource")
218 WindowedSeekableByteChannel instance = new WindowedSeekableByteChannel(getChannel(26), 50);
219 assertEquals(26, instance.getMaxPosition());
220 assertEquals(0L, instance.position());
221
222 instance = new WindowedSeekableByteChannel(getChannel(26), 13);
223 assertEquals(14, instance.getMaxPosition());
224
225 final ByteBuffer b = ByteBuffer.allocate(20);
226 assertTrue(instance.read(b) == 20 && instance.position() == 20);
227 boolean eofEx = false;
228 try {
229 instance.position(Integer.MAX_VALUE);
230 } catch (EOFException ex) {
231 eofEx = true;
232 }
233 assertTrue(eofEx);
234 }
235
236
237 @Test
238 void testUnsupportedOps() throws Exception {
239 boolean hitEx = false;
240 try {
241 @SuppressWarnings("resource")
242 final WindowedSeekableByteChannel instance = new WindowedSeekableByteChannel(getChannel(26), 50);
243 instance.write(ByteBuffer.allocate(10));
244 } catch (UnsupportedOperationException e) {
245 hitEx = true;
246 }
247 assertTrue(hitEx);
248
249 hitEx = false;
250 try {
251 @SuppressWarnings("resource")
252 final WindowedSeekableByteChannel instance = new WindowedSeekableByteChannel(getChannel(26), 50);
253 instance.truncate(10L);
254
255 } catch (UnsupportedOperationException e) {
256 hitEx = true;
257 }
258 assertTrue(hitEx);
259 }
260 }