View Javadoc
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      // lower case 'a'
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      // build array containing sequence(s) of a-z
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       * Test of close method, of class WindowedSeekableByteChannel.
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       * Test of read method, of class WindowedSeekableByteChannel.
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          // 2 full reads will get us to the end
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      * Test using odd values for all buffer sizes.
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      * Test of read into odd dest Buff with even window buffer size.
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      * Test reading into even size dst with odd size window.
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      * Test of position method, of class WindowedSeekableByteChannel.
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         // set past max in memory
199         instance.position(26);
200         b.flip();
201         instance.read(b);
202         // should go back to A
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     /* mostly for coverage */
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 }