View Javadoc
1   package emissary.core.channels;
2   
3   import emissary.test.core.junit5.UnitTest;
4   
5   import org.apache.commons.io.IOUtils;
6   import org.junit.jupiter.api.Test;
7   
8   import java.io.ByteArrayInputStream;
9   import java.io.IOException;
10  import java.io.InputStream;
11  import java.nio.ByteBuffer;
12  import java.nio.channels.SeekableByteChannel;
13  import java.nio.charset.StandardCharsets;
14  
15  import static emissary.core.channels.InputStreamChannelFactory.SIZE_IS_UNKNOWN;
16  import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
17  import static org.junit.jupiter.api.Assertions.assertEquals;
18  import static org.junit.jupiter.api.Assertions.assertFalse;
19  import static org.junit.jupiter.api.Assertions.assertThrows;
20  
21  class InputStreamChannelFactoryTest extends UnitTest {
22      private static class TestInputStreamFactory implements InputStreamFactory {
23          private final byte[] bytes;
24  
25          public TestInputStreamFactory(final byte[] bytes) {
26              this.bytes = bytes;
27          }
28  
29          @Override
30          public InputStream create() {
31              return new ByteArrayInputStream(bytes);
32          }
33      }
34  
35      private static final byte[] testBytes = "0123456789".getBytes(StandardCharsets.UTF_8);
36      private static final byte[] emojiBytes = "😃0123456789".getBytes(StandardCharsets.UTF_16);
37  
38  
39      @Test
40      void testReadNonUtf8() throws IOException {
41          try (SeekableByteChannel sbc = InputStreamChannelFactory.create(emojiBytes.length, new TestInputStreamFactory(emojiBytes)).create()) {
42              final ByteBuffer byteBuffer = ByteBuffer.allocate(8);
43  
44              sbc.position(8);
45              assertEquals(8, IOUtils.read(sbc, byteBuffer));
46              assertEquals("1234", new String(byteBuffer.array(), StandardCharsets.UTF_16));
47  
48              byteBuffer.position(0);
49              sbc.position(0);
50              assertEquals(8, IOUtils.read(sbc, byteBuffer));
51              assertEquals("😃0", new String(byteBuffer.array(), StandardCharsets.UTF_16));
52          }
53      }
54  
55      @Test
56      void testExhaustively() throws IOException {
57          ChannelTestHelper.checkByteArrayAgainstSbc(testBytes,
58                  InputStreamChannelFactory.create(testBytes.length, new TestInputStreamFactory(testBytes)));
59      }
60  
61      @Test
62      void testClose() throws IOException {
63          SeekableByteChannel sbc = InputStreamChannelFactory.create(testBytes.length, new TestInputStreamFactory(testBytes)).create();
64          // Call close twice - should not do or throw anything beyond the first time.
65          sbc.close();
66          assertFalse(sbc.isOpen());
67  
68          assertDoesNotThrow(() -> sbc.close());
69          assertFalse(sbc.isOpen());
70      }
71  
72      @Test
73      void testRead() throws IOException {
74          try (SeekableByteChannel sbc = InputStreamChannelFactory.create(testBytes.length, new TestInputStreamFactory(testBytes)).create()) {
75              final ByteBuffer byteBuffer = ByteBuffer.allocate(1);
76  
77              // Read an arbitrary position
78              sbc.position(5);
79              assertEquals(1, IOUtils.read(sbc, byteBuffer));
80              assertEquals('5', byteBuffer.get(0));
81  
82              byteBuffer.position(0);
83              sbc.position(0);
84              assertEquals(1, IOUtils.read(sbc, byteBuffer));
85              assertEquals('0', byteBuffer.get(0));
86          }
87      }
88  
89      @Test
90      void testSize() throws IOException {
91          // Normal path
92          try (SeekableByteChannel sbc = InputStreamChannelFactory.create(testBytes.length, new TestInputStreamFactory(testBytes)).create()) {
93              ByteBuffer buff = ByteBuffer.allocate(32);
94              assertEquals(testBytes.length, sbc.size());
95              assertEquals(testBytes.length, sbc.read(buff));
96              assertEquals(testBytes.length, buff.position());
97          }
98      }
99  
100     @Test
101     void testCanWorkOutSize() throws IOException {
102         // Make the factory work it out
103         try (SeekableByteChannel sbc = InputStreamChannelFactory.create(-1, new TestInputStreamFactory(testBytes)).create()) {
104             ByteBuffer buff = ByteBuffer.allocate(32);
105             assertEquals(testBytes.length, sbc.size());
106             assertEquals(testBytes.length, sbc.read(buff));
107             assertEquals(testBytes.length, buff.position());
108         }
109     }
110 
111     @Test
112     void testReadWithIncorrectSize() throws IOException {
113         // You can set the size incorrectly, but this is still 'valid'
114         final int sbcLength = testBytes.length + 8;
115         try (SeekableByteChannel sbc = InputStreamChannelFactory.create(sbcLength, new TestInputStreamFactory(testBytes)).create()) {
116             ByteBuffer buff = ByteBuffer.allocate(32);
117             assertEquals(sbcLength, sbc.size());
118             assertEquals(testBytes.length, sbc.read(buff));
119             assertEquals(testBytes.length, buff.position());
120         }
121     }
122 
123     @Test
124     void testReadWithZeroSize() throws IOException {
125         // Zero length channel - will always be EOS, not reading anything into the buffer
126         try (SeekableByteChannel sbc = InputStreamChannelFactory.create(0, new TestInputStreamFactory(testBytes)).create()) {
127             ByteBuffer buff = ByteBuffer.allocate(1);
128             assertEquals(0, sbc.size());
129             assertEquals(-1, sbc.read(buff));
130             assertEquals(0, buff.position());
131         }
132     }
133 
134     @Test
135     void testStartReadBeyondActualData() throws IOException {
136         // Set an SBC that is larger than the data we have, ensure an IOException occurs when reading
137         final int sbcLength = testBytes.length + 8;
138         try (SeekableByteChannel sbc = InputStreamChannelFactory.create(sbcLength, new TestInputStreamFactory(testBytes)).create()) {
139             ByteBuffer buff = ByteBuffer.allocate(32);
140             assertEquals(sbcLength, sbc.size());
141             // Set position beyond length
142             sbc.position(testBytes.length + 2);
143             assertThrows(IOException.class, () -> sbc.read(buff));
144             assertEquals(0, buff.position());
145         }
146     }
147 
148     @Test
149     void testReadWithLargerThanDefinedSize() throws IOException {
150         final int sbcLength = testBytes.length / 2;
151         // Set an SBC that is smaller than the amount of data we have, ensure that we can't read more than the defined size
152         try (SeekableByteChannel sbc = InputStreamChannelFactory.create(sbcLength, new TestInputStreamFactory(testBytes)).create()) {
153             ByteBuffer buff = ByteBuffer.allocate(32);
154             assertEquals(sbcLength, sbc.size());
155             sbc.read(buff);
156             assertEquals(sbcLength, buff.position());
157         }
158     }
159 
160     @Test
161     void testExceptionInputStreamFactory() throws Exception {
162         final InputStreamFactory isf = new InputStreamFactory() {
163             @Override
164             public InputStream create() throws IOException {
165                 throw new IOException("This InputStreamFactory only throws IOExceptions!");
166             }
167         };
168         final SeekableByteChannelFactory sbcf = InputStreamChannelFactory.create(SIZE_IS_UNKNOWN, isf);
169         final ByteBuffer byteBuffer = ByteBuffer.allocate(100);
170 
171         try (SeekableByteChannel sbc = sbcf.create()) {
172             assertThrows(IOException.class, sbc::size);
173             assertThrows(IOException.class, () -> sbc.read(byteBuffer));
174         }
175     }
176 }